Skip to content

Commit 946b20d

Browse files
authored
Merge pull request #1554 from nagilson/nagilson-does-install-still-exist
Improve bugs with installKey management
2 parents 9e31011 + 6e264bd commit 946b20d

File tree

6 files changed

+66
-19
lines changed

6 files changed

+66
-19
lines changed

vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
6060
private readonly dotnetExecutable: string;
6161
private globalResolver: GlobalInstallerResolver | null;
6262

63-
private acquisitionPromises: { [installKeys: string]: Promise<string> | undefined };
63+
private acquisitionPromises: { [installKey: string]: Promise<string> | undefined };
6464
private extensionContext : IVSCodeExtensionContext;
6565

66+
// @member usingNoInstallInvoker - Only use this for test when using the No Install Invoker to fake the worker into thinking a path is on disk.
67+
protected usingNoInstallInvoker = false;
68+
6669
constructor(protected readonly context: IAcquisitionWorkerContext, private readonly utilityContext : IUtilityContext, extensionContext : IVSCodeExtensionContext) {
6770
const dotnetExtension = os.platform() === 'win32' ? '.exe' : '';
6871
this.dotnetExecutable = `dotnet${dotnetExtension}`;
@@ -79,8 +82,14 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
7982

8083
this.removeFolderRecursively(this.context.installDirectoryProvider.getStoragePath());
8184

82-
await this.context.extensionState.update(this.installingVersionsKey, []);
83-
await this.context.extensionState.update(this.installedVersionsKey, []);
85+
// This does not uninstall global things yet, so don't remove their keys.
86+
const installingVersions = this.context.extensionState.get<string[]>(this.installingVersionsKey, []);
87+
const remainingInstallingVersions = installingVersions.filter(x => this.isGlobalInstallKey(x));
88+
await this.context.extensionState.update(this.installingVersionsKey, remainingInstallingVersions);
89+
90+
const installedVersions = this.context.extensionState.get<string[]>(this.installedVersionsKey, []);
91+
const remainingInstalledVersions = installedVersions.filter(x => this.isGlobalInstallKey(x));
92+
await this.context.extensionState.update(this.installedVersionsKey, remainingInstalledVersions);
8493

8594
this.context.eventStream.post(new DotnetUninstallAllCompleted());
8695
}
@@ -100,6 +109,11 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
100109
return this.acquire(await installerResolver.getFullySpecifiedVersion(), false, installerResolver);
101110
}
102111

112+
private isGlobalInstallKey(installKey : string) : boolean
113+
{
114+
return installKey.toLowerCase().includes('global');
115+
}
116+
103117
/**
104118
*
105119
* @remarks this is simply a wrapper around the acquire function.
@@ -109,7 +123,15 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
109123
return this.acquire(version, true, undefined, invoker);
110124
}
111125

112-
public async acquireStatus(version: string, installRuntime: boolean, architecture? : string): Promise<IDotnetAcquireResult | undefined> {
126+
/**
127+
*
128+
* @param version The version of the runtime or sdk to check
129+
* @param installRuntime Whether this is a local runtime status check or a local SDK status check.
130+
* @param architecture The architecture of the install. Undefined means it will be the default arch, which is the node platform arch.
131+
* @returns The result of the install with the path to dotnet if installed, else undefined.
132+
*/
133+
public async acquireStatus(version: string, installRuntime: boolean, architecture? : string): Promise<IDotnetAcquireResult | undefined>
134+
{
113135
const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, architecture ? architecture : this.installingArchitecture)
114136

115137
const existingAcquisitionPromise = this.acquisitionPromises[installKey];
@@ -129,7 +151,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
129151
installedVersions = await this.managePreinstalledVersion(dotnetInstallDir, installedVersions);
130152
}
131153

132-
if (installedVersions.includes(installKey) && fs.existsSync(dotnetPath))
154+
if (installedVersions.includes(installKey) && (fs.existsSync(dotnetPath) || this.usingNoInstallInvoker ))
133155
{
134156
// Requested version has already been installed.
135157
this.context.eventStream.post(new DotnetAcquisitionStatusResolved(installKey, version));
@@ -170,6 +192,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
170192
Debugging.log(`The Acquisition Worker has Determined a Global Install was requested.`, this.context.eventStream);
171193

172194
acquisitionPromise = this.acquireGlobalCore(globalInstallerResolver, installKey).catch((error: Error) => {
195+
this.removeVersionFromExtensionState(this.installingVersionsKey, installKey);
173196
delete this.acquisitionPromises[installKey];
174197
throw new Error(`.NET Acquisition Failed: ${error.message}`);
175198
});
@@ -179,11 +202,14 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
179202
Debugging.log(`The Acquisition Worker has Determined a Local Install was requested.`, this.context.eventStream);
180203

181204
acquisitionPromise = this.acquireLocalCore(version, installRuntime, installKey, localInvoker!).catch((error: Error) => {
205+
this.removeVersionFromExtensionState(this.installingVersionsKey, installKey);
182206
delete this.acquisitionPromises[installKey];
183207
throw new Error(`.NET Acquisition Failed: ${error.message}`);
184208
});
185209
}
186210

211+
// Put this promise into the list so we can let other requests run at the same time
212+
// Allows us to return the end result of this current request for any following duplicates while we are still running.
187213
this.acquisitionPromises[installKey] = acquisitionPromise;
188214
return acquisitionPromise.then((res) => ({ dotnetPath: res }));
189215
}
@@ -229,7 +255,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
229255
installedVersions = await this.managePreinstalledVersion(dotnetInstallDir, installedVersions);
230256
}
231257

232-
if (installedVersions.includes(installKey) && fs.existsSync(dotnetPath)) {
258+
if (installedVersions.includes(installKey) && (fs.existsSync(dotnetPath) || this.usingNoInstallInvoker)) {
233259
// Version requested has already been installed.
234260
this.context.installationValidator.validateDotnetInstall(installKey, dotnetPath);
235261
this.context.eventStream.post(new DotnetAcquisitionAlreadyInstalled(installKey,
@@ -261,6 +287,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
261287
await this.removeVersionFromExtensionState(this.installingVersionsKey, installKey);
262288
await this.addVersionToExtensionState(this.installedVersionsKey, installKey);
263289

290+
delete this.acquisitionPromises[installKey];
264291
return dotnetPath;
265292
}
266293

@@ -280,7 +307,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
280307
}
281308
if(uninstallLocalSDK)
282309
{
283-
await this.uninstallRuntimeOrSDK(installKey);
310+
await this.uninstallLocalRuntimeOrSDK(installKey);
284311
}
285312
}
286313
}
@@ -330,6 +357,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
330357
await this.addVersionToExtensionState(this.installedVersionsKey, installKey);
331358

332359
this.context.eventStream.post(new DotnetGlobalAcquisitionCompletionEvent(`The version ${installKey} completed successfully.`));
360+
delete this.acquisitionPromises[installKey];
333361
return installedSDKPath;
334362
}
335363

@@ -366,7 +394,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
366394
if(legacyInstall.includes(version))
367395
{
368396
this.context.eventStream.post(new DotnetLegacyInstallRemovalRequestEvent(`Trying to remove legacy install: ${legacyInstall} of ${version}.`));
369-
await this.uninstallRuntimeOrSDK(legacyInstall);
397+
await this.uninstallLocalRuntimeOrSDK(legacyInstall);
370398
}
371399
}
372400
}
@@ -398,7 +426,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
398426
{
399427
this.context.eventStream.post(new DotnetInstallGraveyardEvent(
400428
`Attempting to remove .NET at ${installKey} again, as it was left in the graveyard.`));
401-
await this.uninstallRuntimeOrSDK(installKey);
429+
await this.uninstallLocalRuntimeOrSDK(installKey);
402430
}
403431
}
404432

@@ -425,7 +453,13 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
425453
await this.context.extensionState.update(this.installPathsGraveyardKey, graveyard);
426454
}
427455

428-
public async uninstallRuntimeOrSDK(installKey : string) {
456+
public async uninstallLocalRuntimeOrSDK(installKey : string)
457+
{
458+
if(this.isGlobalInstallKey(installKey))
459+
{
460+
return;
461+
}
462+
429463
try
430464
{
431465
delete this.acquisitionPromises[installKey];

vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ export class LinuxVersionResolver
175175
{
176176
// Implement any custom logic for a Distro Class in a new DistroSDKProvider and add it to the factory here.
177177
case null:
178-
const error = new DotnetAcquisitionDistroUnknownError(new Error(this.baseUnsupportedDistroErrorMessage), getInstallKeyFromContext(this.acquireContext));
179-
this.acquisitionContext.eventStream.post(error);
180-
throw error.error;
178+
const unknownDistroErr = new DotnetAcquisitionDistroUnknownError(new Error(this.baseUnsupportedDistroErrorMessage), getInstallKeyFromContext(this.acquireContext));
179+
this.acquisitionContext.eventStream.post(unknownDistroErr);
180+
throw unknownDistroErr.error;
181181
case 'Red Hat Enterprise Linux':
182182
if(this.isRedHatVersion7(distroAndVersion.version))
183183
{

vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { ITelemetryReporter } from '../../EventStream/TelemetryObserver';
3636
import { IUtilityContext } from '../../Utils/IUtilityContext';
3737
import { IVSCodeEnvironment } from '../../Utils/IVSCodeEnvironment';
3838
import { IDotnetAcquireResult } from '../../IDotnetAcquireResult';
39+
import { IDotnetCoreAcquisitionWorker } from '../../Acquisition/IDotnetCoreAcquisitionWorker';
3940

4041
const testDefaultTimeoutTimeMs = 60000;
4142
/* tslint:disable:no-any */
@@ -81,6 +82,13 @@ export class NoInstallAcquisitionInvoker extends IAcquisitionInvoker {
8182

8283
});
8384
}
85+
86+
constructor(eventStream : IEventStream, worker : MockDotnetCoreAcquisitionWorker)
87+
{
88+
super(eventStream);
89+
worker.enableNoInstallInvoker();
90+
}
91+
8492
}
8593

8694
export class MockDotnetCoreAcquisitionWorker extends DotnetCoreAcquisitionWorker
@@ -115,6 +123,11 @@ export class MockDotnetCoreAcquisitionWorker extends DotnetCoreAcquisitionWorker
115123
this.context.installingArchitecture = newArch;
116124
this.context.acquisitionContext!.architecture = newArch;
117125
}
126+
127+
public enableNoInstallInvoker()
128+
{
129+
this.usingNoInstallInvoker = true;
130+
}
118131
}
119132

120133
export class RejectingAcquisitionInvoker extends IAcquisitionInvoker {

vscode-dotnet-runtime-library/src/test/unit/DotnetCoreAcquisitionWorker.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
4444
const context = new MockExtensionContext();
4545
const eventStream = new MockEventStream();
4646
const acquisitionWorker = getMockAcquisitionWorker(isRuntimeWorker, version, arch, eventStream, context);
47-
const invoker = new NoInstallAcquisitionInvoker(eventStream);
47+
const invoker = new NoInstallAcquisitionInvoker(eventStream, acquisitionWorker);
4848
return [acquisitionWorker, eventStream, context, invoker];
4949
}
5050

@@ -140,7 +140,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
140140
assert.exists(undefinedEvent, 'Undefined event exists');
141141

142142
await acquisitionWorker.acquireSDK(version, invoker);
143-
result = await acquisitionWorker.acquireStatus(version, false);
143+
result = await acquisitionWorker.acquireStatus(version, false, undefined);
144144
await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, context, false);
145145
const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved);
146146
assert.exists(resolvedEvent, 'The sdk is resolved');
@@ -155,8 +155,8 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
155155
const undefinedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusUndefined);
156156
assert.exists(undefinedEvent);
157157

158-
await acquisitionWorker.acquireSDK(version, invoker);
159-
result = await acquisitionWorker.acquireStatus(version, true);
158+
await acquisitionWorker.acquireRuntime(version, invoker);
159+
result = await acquisitionWorker.acquireStatus(version, true, undefined);
160160
await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, context, true);
161161
const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved);
162162
assert.exists(resolvedEvent);

vscode-dotnet-runtime-library/src/test/unit/WinMacGlobalInstaller.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as chai from 'chai';
77
import * as os from 'os';
88
import * as fs from 'fs';
99
import * as path from 'path';
10-
import { MockCommandExecutor, MockEventStream, MockExtensionContext, MockFileUtilities, MockInstallationValidator, NoInstallAcquisitionInvoker } from '../mocks/MockObjects';
10+
import { MockCommandExecutor, MockFileUtilities } from '../mocks/MockObjects';
1111
import { WinMacGlobalInstaller } from '../../Acquisition/WinMacGlobalInstaller';
1212
import { FileUtilities } from '../../Utils/FileUtilities';
1313
import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility';

vscode-dotnet-sdk-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
160160
fs.writeFileSync(dotnetExePath, '');
161161

162162
// Assert preinstalled SDKs are detected
163-
const acquisitionInvoker = new NoInstallAcquisitionInvoker(eventStream);
163+
const acquisitionInvoker = new NoInstallAcquisitionInvoker(eventStream, acquisitionWorker);
164164
const result = await acquisitionWorker.acquireSDK(version, acquisitionInvoker);
165165
assert.equal(path.dirname(result.dotnetPath), dotnetDir);
166166
const preinstallEvents = eventStream.events

0 commit comments

Comments
 (0)