Permit Installing .NET via a package.json file (#1976)
* Permit Installing .NET via a package.json file Extensions that rely on .NET often have a slow startup time due to our existing API. For extensions that want the .NET runtime to be installed as soon as possible, they can now also make an API request in their package.json. They should add a [IDotnetAcquireContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts) object in a section titled 'x-dotnet-acquire' to do so. They don't need to include the `requestingExtensionId`, since this is already in their `package.json`, This should go at the root of a `package.json`, and not in the `contributes` section. ``` "x-dotnet-acquire": { "version": "8.0", "mode": "aspnetcore" } ``` We run the install on startup and also whenever a new extension is installed via the onDidChange API. https://code.visualstudio.com/api/references/vscode-api#Extension<T> You can easily test this by modifying the SDK extension and adding this, then packging t with `vsce`, installing it, and running the Runtime Extension under the Debug tab. If you do so, you will see that: If you install a new extension, the request is made. If you had already installed the extension, the request is also made. * Respond to linter * Add a very basic test. * Fix typo * Fix typo
This commit is contained in:
Родитель
40d1a8bc66
Коммит
2a876ace5d
|
@ -4,7 +4,11 @@ This article outlines the commands exposed by the .NET Install Tool. To see thes
|
|||
|
||||
## dotnet.acquire
|
||||
|
||||
This command will install a .NET runtime. It accepts a [IDotnetAcquireContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts) object and returns a [IDotnetAcquireResult](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireResult.ts), which contains the path to the .NET runtime executable. Note that the version value in IDotnetAcquireContext must be a valid major.minor runtime version, for example 3.1. The extension will automatically identify and install the latest patch of the provided version. It is generally recommended that extension authors call this command immedietly on every extension start up to ensure that the .NET runtime has been installed and is ready to use.
|
||||
This command will install a .NET runtime. It accepts a [IDotnetAcquireContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts) object and returns a [IDotnetAcquireResult](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireResult.ts), which contains the path to the .NET runtime executable. Note that the version value in IDotnetAcquireContext must be a valid major.minor runtime version, for example 3.1. The extension will automatically identify and install the latest patch of the provided version. It is generally recommended that extension authors call this command immediately on every extension start up to ensure that the .NET runtime has been installed and is ready to use.
|
||||
|
||||
## dotnet.acquireGlobalSDK
|
||||
|
||||
This command will install a .NET SDK globally. It accepts a [IDotnetAcquireContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts) object and returns a [IDotnetAcquireResult](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireResult.ts).
|
||||
|
||||
## dotnet.showAcquisitionLog
|
||||
|
||||
|
@ -13,3 +17,34 @@ This command surfaces an output channel to the user which provides status messag
|
|||
## dotnet.ensureDotnetDependencies
|
||||
|
||||
This command is only applicable to linux machines. It attempts to ensure that .NET dependencies are present and, if they are not, installs them or prompts the user to do so. It accepts a [IDotnetEnsureDependenciesContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetEnsureDependenciesContext.ts) object and has a void return type.
|
||||
|
||||
## dotnet.findPath
|
||||
|
||||
You can execute this command to return a string to a dotnet runtime path. Pass it a [IDotnetFindPathContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetFindPathContext.ts) object. The major.minor will be respected based on the requirement you give (greater_than_or_equal will return a dotnet on the PATH that is >= the version requested.)
|
||||
|
||||
This returns undefined if no matches are found.
|
||||
|
||||
## dotnet.uninstall
|
||||
|
||||
You can execute this command to dereference / uninstall .NET, either the SDK or runtime, as long as it's managed by this extension. Pass it a [IDotnetFindPathContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetFindPathContext.ts) object suggesting which install you want to remove.
|
||||
|
||||
.NET will only be completely uninstalled if all extensions that relied on that version of .NET asked for it to be uninstalled.
|
||||
Note that users can manually uninstall any version of .NET if they so chose and accept the risk.
|
||||
|
||||
# JSON Installation
|
||||
|
||||
If you want the .NET runtime to be installed as soon as possible, you can also make an API request in your package.json.
|
||||
Add a [IDotnetAcquireContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts) object in a section titled 'x-dotnet-acquire' to do so. You don't need to include the `requestingExtensionId`.
|
||||
|
||||
This should go at the root of your `package.json`, and not in the `contributes` section.
|
||||
|
||||
When any extension is changed, or on startup, we will try to fulfill these requests.
|
||||
The disadvantage to this approach is you cannot respond to a failure to install, check the status before making the request,
|
||||
lookup the PATH first to see if there's a matching install, etc.
|
||||
|
||||
```json
|
||||
"x-dotnet-acquire": {
|
||||
"version": "8.0",
|
||||
"mode": "aspnetcore"
|
||||
}
|
||||
```
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
[![Version](https://img.shields.io/visual-studio-marketplace/v/ms-dotnettools.vscode-dotnet-runtime?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscode-dotnet-runtime) [![Installs](https://img.shields.io/visual-studio-marketplace/i/ms-dotnettools.vscode-dotnet-runtime?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscode-dotnet-runtime)
|
||||
|
||||
This extension provides a unified way for other extensions like the [C#] and [C# Dev Kit] extensions to install local versions of the .NET Runtime, and machine-wide versions of the .NET SDK. Those extensions tell the .NET Install Tool when they would like a .NET SDK to be on the machine, and we install one for them if there's not already one that matches the SDK they need to run properly. In the future, this tool may support allowing users to call the API via VS Code to install .NET and pick a version of the SDK to install themselves.
|
||||
This extension provides a unified way for other extensions like the [C#] and [C# Dev Kit] extensions to install local versions of the .NET Runtime, and machine-wide versions of the .NET SDK. Those extensions tell the .NET Install Tool when they would like a .NET SDK to be on the machine, and we install one for them if there's not already one that matches the SDK they need to run properly. Users can also install the .NET SDK themselves by reading below.
|
||||
|
||||
|
||||
## Why do I have this extension?
|
||||
|
||||
|
@ -17,10 +18,9 @@ This extension was probably included as a dependency of one of the following ext
|
|||
|
||||
The above extensions call into this extension to provide a unified way of downloading shared .NET Runtimes or .NET SDKs. If you already have an installation of .NET that you'd like to use, see [the troubleshooting section below](#i-already-have-a-net-runtime-or-sdk-installed-and-i-want-to-use-it). If you want to remove this extension completely, you will need to uninstall any extensions that depend on it first. If this extension is uninstalled, any .NET Runtimes installed by it will also be removed.
|
||||
|
||||
## [Preview] Using the extension yourself
|
||||
## Using the extension yourself
|
||||
|
||||
As of version 2.0.2, you can install the .NET SDK using part of our private API via the VS Code Command Palette!
|
||||
This feature is in preview and still undergoing testing.
|
||||
|
||||
To use the feature:
|
||||
Bring up the command palette (ctrl + shift + p) and run the command:
|
||||
|
@ -29,7 +29,7 @@ Bring up the command palette (ctrl + shift + p) and run the command:
|
|||
![Video demonstrating use of the command pallet to install .NET.](https://raw.githubusercontent.com/dotnet/vscode-dotnet-runtime/63b7fca6c714781dc4cb1cdbcb786013f2115098/Documentation/example.gif)
|
||||
|
||||
The command will try to find the best version of .NET for you to install, but you can tell it to install other versions as well based on its prompt.
|
||||
Note this feature is in preview, and does not support all distros, WSL, nor preview or RC versions of .NET.
|
||||
Note this feature does not support all distros, WSL, nor preview or RC versions of .NET.
|
||||
|
||||
The rest of the extension functionality is still limited to other extensions that rely on our extension.
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ import {
|
|||
DotnetFindPathDidNotMeetCondition,
|
||||
DotnetFindPathMetCondition,
|
||||
DotnetFindPathCommandInvoked,
|
||||
JsonInstaller,
|
||||
} from 'vscode-dotnet-runtime-library';
|
||||
import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId';
|
||||
import { InstallTrackerSingleton } from 'vscode-dotnet-runtime-library/dist/Acquisition/InstallTrackerSingleton';
|
||||
|
@ -757,6 +758,9 @@ We will try to install .NET, but are unlikely to be able to connect to the serve
|
|||
return null;
|
||||
}
|
||||
|
||||
// Preemptively install .NET for extensions who tell us to in their package.json
|
||||
const jsonInstaller = new JsonInstaller(globalEventStream, vsCodeExtensionContext);
|
||||
|
||||
// Exposing API Endpoints
|
||||
vsCodeContext.subscriptions.push(
|
||||
dotnetAcquireRegistration,
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Licensed to the .NET Foundation under one or more agreements.
|
||||
* The .NET Foundation licenses this file to you under the MIT license.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IEventStream } from '../EventStream/EventStream';
|
||||
import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
|
||||
|
||||
export abstract class IJsonInstaller
|
||||
{
|
||||
constructor(protected readonly eventStream: IEventStream, protected readonly vscodeAccessor : IVSCodeExtensionContext) {}
|
||||
|
||||
public abstract executeJSONRequests(): Promise<void>;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Licensed to the .NET Foundation under one or more agreements.
|
||||
* The .NET Foundation licenses this file to you under the MIT license.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IEventStream } from "../EventStream/EventStream";
|
||||
import { DotnetVSCodeExtensionChange, DotnetVSCodeExtensionFound, DotnetVSCodeExtensionHasInstallRequest } from "../EventStream/EventStreamEvents";
|
||||
import { IDotnetAcquireContext } from "../IDotnetAcquireContext";
|
||||
import { IVSCodeExtensionContext } from "../IVSCodeExtensionContext";
|
||||
import { IJsonInstaller } from "./IJsonInstaller";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
export class JsonInstaller extends IJsonInstaller
|
||||
{
|
||||
constructor(protected readonly eventStream: IEventStream, protected readonly vscodeAccessor : IVSCodeExtensionContext)
|
||||
{
|
||||
super(eventStream, vscodeAccessor);
|
||||
// If a new extension is installed, we want to install .NET preemptively for it if specified
|
||||
vscodeAccessor.registerOnExtensionChange(() =>
|
||||
{
|
||||
this.eventStream.post(new DotnetVSCodeExtensionChange(`A change was detected in the extensions. Installing .NET for new extensions.`));
|
||||
this.executeJSONRequests().catch( () => {});
|
||||
})
|
||||
|
||||
// On startup, (our extension gets activated onStartupFinished() via 'activationEvents' in package.json) we want to install .NET preemptively
|
||||
// So other extensions can have a faster startup time if they so desire
|
||||
this.executeJSONRequests().catch( () => {});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async executeJSONRequests(): Promise<void>
|
||||
{
|
||||
const extensions = this.vscodeAccessor.getExtensions();
|
||||
for(const extension of extensions)
|
||||
{
|
||||
const extensionPackage = extension?.packageJSON;
|
||||
this.eventStream.post(new DotnetVSCodeExtensionFound(`Checking extension ${extension?.id} for .NET installation requests`));
|
||||
|
||||
if(extensionPackage['x-dotnet-acquire'])
|
||||
{
|
||||
this.eventStream.post(new DotnetVSCodeExtensionHasInstallRequest(`Installing .NET for extension ${extension.id}`));
|
||||
const jsonRequest = (extensionPackage as { "x-dotnet-acquire": Omit<IDotnetAcquireContext, "requestingExtensionId"> })["x-dotnet-acquire"];
|
||||
const apiRequest : IDotnetAcquireContext = { ...jsonRequest, requestingExtensionId: extension.id };
|
||||
this.vscodeAccessor.executeCommand('dotnet.acquire', apiRequest);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -876,6 +876,18 @@ export class DotnetTelemetrySettingEvent extends DotnetCustomMessageEvent {
|
|||
public readonly eventName = 'DotnetTelemetrySettingEvent';
|
||||
}
|
||||
|
||||
export class DotnetVSCodeExtensionFound extends DotnetCustomMessageEvent {
|
||||
public readonly eventName = 'DotnetVSCodeExtensionFound';
|
||||
}
|
||||
|
||||
export class DotnetVSCodeExtensionHasInstallRequest extends DotnetCustomMessageEvent {
|
||||
public readonly eventName = 'DotnetVSCodeExtensionHasInstallRequest';
|
||||
}
|
||||
|
||||
export class DotnetVSCodeExtensionChange extends DotnetCustomMessageEvent {
|
||||
public readonly eventName = 'DotnetVSCodeExtensionChange';
|
||||
}
|
||||
|
||||
export class DotnetCommandNotFoundEvent extends DotnetCustomMessageEvent {
|
||||
public readonly eventName = 'DotnetCommandNotFoundEvent';
|
||||
}
|
||||
|
|
|
@ -8,4 +8,10 @@ export abstract class IVSCodeExtensionContext
|
|||
abstract setVSCodeEnvironmentVariable(variable : string, value : string) : void;
|
||||
|
||||
abstract appendToEnvironmentVariable(variable : string, pathAdditionWithDelimiter : string) : void;
|
||||
|
||||
abstract registerOnExtensionChange<A extends any[], R>(f: (...args: A) => R, ...args: A) : void;
|
||||
|
||||
abstract getExtensions() : readonly any[];
|
||||
|
||||
abstract executeCommand(command : string, ...args: any[]) : Thenable<any>;
|
||||
}
|
||||
|
|
|
@ -22,9 +22,26 @@ export class VSCodeExtensionContext extends IVSCodeExtensionContext
|
|||
environment.replace(variable, value);
|
||||
}
|
||||
|
||||
appendToEnvironmentVariable(variable: string, appendingValue: string): void
|
||||
public appendToEnvironmentVariable(variable: string, appendingValue: string): void
|
||||
{
|
||||
const environment = this.context.environmentVariableCollection;
|
||||
environment?.append(variable, appendingValue);
|
||||
}
|
||||
|
||||
public registerOnExtensionChange<A extends any[], R>(f: (...args: A) => R, ...args: A): void
|
||||
{
|
||||
vscode.extensions.onDidChange(() => {
|
||||
f(...(args));
|
||||
})
|
||||
}
|
||||
|
||||
public getExtensions() : readonly vscode.Extension<any>[]
|
||||
{
|
||||
return vscode.extensions.all;
|
||||
}
|
||||
|
||||
public executeCommand(command : string, ...args: any[]) : Thenable<any>
|
||||
{
|
||||
return vscode.commands.executeCommand(command, ...args);
|
||||
}
|
||||
}
|
|
@ -39,6 +39,8 @@ export * from './Acquisition/DotnetCoreAcquisitionWorker';
|
|||
export * from './Acquisition/DotnetConditionValidator';
|
||||
export * from './Acquisition/IDotnetPathFinder';
|
||||
export * from './Acquisition/DotnetPathFinder';
|
||||
export * from './Acquisition/IJsonInstaller';
|
||||
export * from './Acquisition/JsonInstaller';
|
||||
export * from './Acquisition/IDotnetConditionValidator';
|
||||
export * from './Acquisition/IDotnetListInfo';
|
||||
export * from './Acquisition/DotnetInstall';
|
||||
|
|
|
@ -226,6 +226,19 @@ export class MockIndexWebRequestWorker extends WebRequestWorker {
|
|||
|
||||
export class MockVSCodeExtensionContext extends IVSCodeExtensionContext
|
||||
{
|
||||
registerOnExtensionChange<A extends any[], R>(f: (...args: A) => R, ...args: A): void {
|
||||
f(...args);
|
||||
}
|
||||
|
||||
getExtensions(): readonly any[]
|
||||
{
|
||||
return [{ extensionId : 'test'}];
|
||||
}
|
||||
|
||||
executeCommand(command: string, ...args: any[]): Thenable<any> {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
appendToEnvironmentVariable(variable: string, pathAdditionWithDelimiter: string): void {
|
||||
// Do nothing.
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Licensed to the .NET Foundation under one or more agreements.
|
||||
* The .NET Foundation licenses this file to you under the MIT license.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as chai from 'chai';
|
||||
import { JsonInstaller } from '../../Acquisition/JsonInstaller';
|
||||
import { MockEventStream, MockVSCodeExtensionContext } from '../mocks/MockObjects';
|
||||
import { DotnetVSCodeExtensionFound } from '../../EventStream/EventStreamEvents';
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
suite('JSONInstaller Unit Tests', () => {
|
||||
const eventStream = new MockEventStream();
|
||||
const mockContext = new MockVSCodeExtensionContext();
|
||||
|
||||
test('It Scans Extensions Without x-dotnet-acquire', async () =>
|
||||
{
|
||||
const _ = new JsonInstaller(eventStream,mockContext);
|
||||
const acquireEvent = eventStream.events.find(event => event instanceof DotnetVSCodeExtensionFound) as DotnetVSCodeExtensionFound;
|
||||
|
||||
assert.exists(acquireEvent, 'The extensions were scanned, and did not cause an error with having an empty json value')
|
||||
});
|
||||
});
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.7 KiB |
|
@ -56,6 +56,7 @@
|
|||
"@types/vscode": "1.74.0",
|
||||
"@vscode/extension-telemetry": "^0.9.7",
|
||||
"@vscode/sudo-prompt": "^9.3.1",
|
||||
"@vscode/test-electron": "^2.4.1",
|
||||
"axios": "^1.7.4",
|
||||
"axios-cache-interceptor": "^1.5.3",
|
||||
"axios-retry": "^3.4.0",
|
||||
|
@ -71,8 +72,7 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"semver": "^7.6.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"typescript": "^5.5.4",
|
||||
"vscode-test": "^1.6.1"
|
||||
"typescript": "^5.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "4.2.22",
|
||||
|
@ -6383,6 +6383,7 @@
|
|||
"@types/vscode": "1.74.0",
|
||||
"@vscode/extension-telemetry": "^0.9.7",
|
||||
"@vscode/sudo-prompt": "^9.3.1",
|
||||
"@vscode/test-electron": "^2.4.1",
|
||||
"axios": "^1.7.4",
|
||||
"axios-cache-interceptor": "^1.5.3",
|
||||
"axios-retry": "^3.4.0",
|
||||
|
@ -6400,8 +6401,7 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"semver": "^7.6.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"typescript": "^5.5.4",
|
||||
"vscode-test": "^1.6.1"
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
|
|
|
@ -2188,6 +2188,7 @@ util-deprecate@~1.0.1:
|
|||
"@types/vscode" "1.74.0"
|
||||
"@vscode/extension-telemetry" "^0.9.7"
|
||||
"@vscode/sudo-prompt" "^9.3.1"
|
||||
"@vscode/test-electron" "^2.4.1"
|
||||
axios "^1.7.4"
|
||||
axios-cache-interceptor "^1.5.3"
|
||||
axios-retry "^3.4.0"
|
||||
|
@ -2204,7 +2205,6 @@ util-deprecate@~1.0.1:
|
|||
semver "^7.6.2"
|
||||
shelljs "^0.8.5"
|
||||
typescript "^5.5.4"
|
||||
vscode-test "^1.6.1"
|
||||
optionalDependencies:
|
||||
fsevents "^2.3.3"
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче