Skip to content

Commit 63cf4b4

Browse files
authored
fix(core): update-swagger executor always reinstalls tool (#757)
1 parent 12d89ac commit 63cf4b4

File tree

9 files changed

+247
-42
lines changed

9 files changed

+247
-42
lines changed

packages/core/src/executors/format/executor.spec.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import * as devkit from '@nrwl/devkit';
21
import { ExecutorContext } from '@nrwl/devkit';
32

4-
import * as fs from 'fs';
5-
63
import { DotNetClient, mockDotnetFactory } from '@nx-dotnet/dotnet';
74
import * as utils from '@nx-dotnet/utils';
85

@@ -68,7 +65,9 @@ describe('Format Executor', () => {
6865
});
6966

7067
it('installs dotnet-format if not already installed', async () => {
71-
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
68+
jest
69+
.spyOn(utils, 'readInstalledDotnetToolVersion')
70+
.mockReturnValue(undefined);
7271
const res = await executor(options, context, dotnetClient);
7372
expect(
7473
(dotnetClient as jest.Mocked<DotNetClient>).installTool,
@@ -77,11 +76,9 @@ describe('Format Executor', () => {
7776
});
7877

7978
it('does not install dotnet-format if already installed', async () => {
80-
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
8179
jest
82-
.spyOn(devkit, 'readJsonFile')
83-
.mockReturnValue({ tools: { 'dotnet-format': '1.0.0' } });
84-
80+
.spyOn(utils, 'readInstalledDotnetToolVersion')
81+
.mockReturnValue('1.0.0');
8582
const res = await executor(options, context, dotnetClient);
8683
expect(
8784
(dotnetClient as jest.Mocked<DotNetClient>).installTool,
@@ -94,11 +91,6 @@ describe('Format Executor', () => {
9491
'6.0.101',
9592
);
9693

97-
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
98-
jest
99-
.spyOn(devkit, 'readJsonFile')
100-
.mockReturnValue({ tools: { 'dotnet-format': '1.0.0' } });
101-
10294
const res = await executor(options, context, dotnetClient);
10395
expect(
10496
(dotnetClient as jest.Mocked<DotNetClient>).installTool,
@@ -110,11 +102,9 @@ describe('Format Executor', () => {
110102
(dotnetClient as jest.Mocked<DotNetClient>).getSdkVersion.mockReturnValue(
111103
'5.0.101',
112104
);
113-
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
114105
jest
115-
.spyOn(devkit, 'readJsonFile')
116-
.mockReturnValue({ tools: { 'dotnet-format': '1.0.0' } });
117-
106+
.spyOn(utils, 'readInstalledDotnetToolVersion')
107+
.mockReturnValue('1.0.0');
118108
const res = await executor(options, context, dotnetClient);
119109
expect(res.success).toBeTruthy();
120110

packages/core/src/executors/format/executor.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { ExecutorContext, readJsonFile, workspaceRoot } from '@nrwl/devkit';
1+
import { ExecutorContext } from '@nrwl/devkit';
22

3-
import { existsSync } from 'fs';
4-
import { join } from 'path';
53
import * as semver from 'semver';
64

75
import { DotNetClient, dotnetFactory } from '@nx-dotnet/dotnet';
86
import {
97
getExecutedProjectConfiguration,
108
getProjectFileForNxProject,
9+
readInstalledDotnetToolVersion,
1110
} from '@nx-dotnet/utils';
1211

1312
import { FormatExecutorSchema } from './schema';
@@ -60,12 +59,7 @@ function ensureFormatToolInstalled(
6059
dotnetClient: DotNetClient,
6160
majorVersion: number,
6261
) {
63-
const manifestPath = join(workspaceRoot, './.config/dotnet-tools.json');
64-
65-
const manifest = existsSync(manifestPath)
66-
? readJsonFile(manifestPath)
67-
: undefined;
68-
if (manifest?.tools['dotnet-format']) {
62+
if (readInstalledDotnetToolVersion('dotnet-format')) {
6963
// dotnet-format is already installed.
7064
return;
7165
}

packages/core/src/executors/update-swagger/executor.spec.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,19 @@ describe('Update-Swagger Executor', () => {
8888
}
8989
throw new Error('Attempted to read unexpected file');
9090
});
91+
jest
92+
.spyOn(utils, 'readInstalledDotnetToolVersion')
93+
.mockImplementation((tool) => {
94+
if (tool === SWAGGER_CLI_TOOL) {
95+
return '99.99.99';
96+
}
97+
throw new Error('unknown tool version read');
98+
});
9199
jest
92100
.spyOn(devkit, 'readJsonFile')
93101
.mockImplementation((p: string): object => {
94102
if (p === `${root}/.nx-dotnet.rc.json`) {
95103
return {};
96-
} else if (p === `${root}/.config/dotnet-tools.json`) {
97-
return { tools: { [SWAGGER_CLI_TOOL]: '99.99.99' } };
98104
}
99105
throw new Error(`Attempted to read unexpected file: ${p}`);
100106
});
@@ -122,6 +128,14 @@ describe('Update-Swagger Executor', () => {
122128
throw new Error('Attempted to read unexpected file');
123129
});
124130
jest.spyOn(devkit, 'readJsonFile').mockReturnValue({});
131+
jest
132+
.spyOn(utils, 'readInstalledDotnetToolVersion')
133+
.mockImplementation((tool) => {
134+
if (tool === SWAGGER_CLI_TOOL) {
135+
return undefined;
136+
}
137+
throw new Error('unknown tool version read');
138+
});
125139
const res = await executor(options, context, dotnetClient);
126140
expect(
127141
(dotnetClient as jest.Mocked<DotNetClient>).installTool,
@@ -137,13 +151,19 @@ describe('Update-Swagger Executor', () => {
137151
}
138152
throw new Error('Attempted to read unexpected file');
139153
});
154+
jest
155+
.spyOn(utils, 'readInstalledDotnetToolVersion')
156+
.mockImplementation((tool) => {
157+
if (tool === SWAGGER_CLI_TOOL) {
158+
return '99.99.99';
159+
}
160+
throw new Error('unknown tool version read');
161+
});
140162
jest
141163
.spyOn(devkit, 'readJsonFile')
142164
.mockImplementation((p: string): object => {
143165
if (p === `${root}/.nx-dotnet.rc.json`) {
144166
return {};
145-
} else if (p === `${root}/.config/dotnet-tools.json`) {
146-
return { tools: { [SWAGGER_CLI_TOOL]: '99.99.99' } };
147167
}
148168
throw new Error(`Attempted to read unexpected file: ${p}`);
149169
});
@@ -163,6 +183,10 @@ describe('Update-Swagger Executor', () => {
163183
}
164184
throw new Error('Attempted to read unexpected file');
165185
});
186+
const readToolVersionSpy = jest
187+
.spyOn(utils, 'readInstalledDotnetToolVersion')
188+
.mockReset();
189+
166190
jest.spyOn(devkit, 'readJsonFile').mockReturnValue({});
167191
const res = await executor(
168192
{ ...options, skipInstall: true },
@@ -172,6 +196,7 @@ describe('Update-Swagger Executor', () => {
172196
expect(
173197
(dotnetClient as jest.Mocked<DotNetClient>).installTool,
174198
).not.toHaveBeenCalled();
199+
expect(readToolVersionSpy).not.toBeCalled();
175200
expect(res.success).toBeTruthy();
176201
});
177202
});

packages/core/src/executors/update-swagger/executor.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ import {
22
ExecutorContext,
33
logger,
44
ProjectConfiguration,
5-
readJsonFile,
65
workspaceRoot,
76
} from '@nrwl/devkit';
87

9-
import { existsSync } from 'fs';
108
import { ensureDirSync } from 'fs-extra';
11-
import { dirname, join, resolve } from 'path';
9+
import { dirname, resolve } from 'path';
1210

1311
import { DotNetClient, dotnetFactory } from '@nx-dotnet/dotnet';
1412
import {
@@ -17,6 +15,7 @@ import {
1715
iterateChildrenByPath,
1816
readConfig,
1917
readXml,
18+
readInstalledDotnetToolVersion,
2019
} from '@nx-dotnet/utils';
2120

2221
import { buildStartupAssemblyPath } from '../../generators/utils/get-path-to-startup-assembly';
@@ -118,16 +117,15 @@ function ensureSwaggerToolInstalled(
118117
dotnetClient: DotNetClient,
119118
version: string,
120119
) {
121-
const manifestPath = join(workspaceRoot, './.config/dotnet-tools.json');
122-
const manifest = existsSync(manifestPath)
123-
? readJsonFile(manifestPath)
124-
: undefined;
125-
126-
if (manifest?.tools[SWAGGER_CLI_TOOL] === version) {
127-
return;
128-
} else if (manifest?.tools[SWAGGER_CLI_TOOL]) {
120+
const installedSwaggerVersion =
121+
readInstalledDotnetToolVersion(SWAGGER_CLI_TOOL);
122+
123+
if (installedSwaggerVersion) {
124+
if (installedSwaggerVersion === version) {
125+
return;
126+
}
129127
logger.warn(
130-
`Swagger CLI was found, but the version does not match the version of Swashbuckle.AspNetCore in ${context.projectName}. We reinstalled it such that the version matches, but you may want to review the changes made.`,
128+
`Swagger CLI was found, but the version "${installedSwaggerVersion}" does not match the expected version "${version}" of Swashbuckle.AspNetCore in ${context.projectName}. We reinstalled it such that the version matches, but you may want to review the changes made.`,
131129
);
132130
}
133131

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface DotnetToolsManifestV1 {
2+
version: 1;
3+
isRoot: boolean;
4+
tools: {
5+
[key: string]: {
6+
version: string;
7+
commands: string[];
8+
};
9+
};
10+
}
11+
12+
export type DotnetToolsManifest = DotnetToolsManifestV1;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './cmd-line-parameter';
22
export * from './nx-dotnet-config.interface';
33
export * from './nx';
4+
export * from './dotnet-tools-manifest.interface';
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import {
2+
readDotnetToolsManifest,
3+
readInstalledDotnetToolVersion,
4+
} from './dotnet-tools-manifest';
5+
import * as devkit from '@nrwl/devkit';
6+
import * as fs from 'fs';
7+
8+
const root = '/virtual';
9+
jest.mock('@nrwl/devkit', () => ({
10+
...jest.requireActual('@nrwl/devkit'),
11+
workspaceRoot: '/virtual',
12+
}));
13+
14+
const existsSyncMock = jest.spyOn(fs, 'existsSync');
15+
const readJsonFileMock = jest.spyOn(devkit, 'readJsonFile');
16+
17+
describe('dotnet tools util functions', () => {
18+
describe('readDotnetToolsManifest', () => {
19+
beforeEach(() => {
20+
existsSyncMock.mockReset();
21+
readJsonFileMock.mockReset();
22+
existsSyncMock.mockReturnValue(true);
23+
readJsonFileMock.mockImplementation((p: string): object => {
24+
if (p === `${root}/.config/dotnet-tools.json`) {
25+
return {
26+
version: 1,
27+
isRoot: true,
28+
tools: {
29+
'swashbuckle.aspnetcore.cli': {
30+
version: '99.99.99',
31+
commands: ['swagger'],
32+
},
33+
},
34+
};
35+
}
36+
throw new Error(`Attempted to read unexpected file: ${p}`);
37+
});
38+
});
39+
40+
it('should read from workspace root', async () => {
41+
const result = readDotnetToolsManifest();
42+
expect(result).toEqual({
43+
version: 1,
44+
isRoot: true,
45+
tools: {
46+
'swashbuckle.aspnetcore.cli': {
47+
version: '99.99.99',
48+
commands: ['swagger'],
49+
},
50+
},
51+
});
52+
});
53+
54+
it('should return undefined if file missing', async () => {
55+
existsSyncMock.mockReturnValue(false);
56+
const result = readDotnetToolsManifest();
57+
expect(result).toBeUndefined();
58+
expect(readJsonFileMock).not.toHaveBeenCalled();
59+
});
60+
61+
it('should return undefined if file wrong version', async () => {
62+
readJsonFileMock.mockImplementation((p: string): object => {
63+
if (p === `${root}/.config/dotnet-tools.json`) {
64+
return {
65+
version: 99,
66+
isRoot: true,
67+
tools: {},
68+
};
69+
}
70+
throw new Error(`Attempted to read unexpected file: ${p}`);
71+
});
72+
const result = readDotnetToolsManifest();
73+
expect(result).toBeUndefined();
74+
});
75+
76+
it('read from overridden file path if provided', async () => {
77+
readJsonFileMock.mockImplementation((p: string): object => {
78+
if (p === '/custom/path/file.json') {
79+
return {
80+
version: 1,
81+
isRoot: true,
82+
tools: {},
83+
};
84+
}
85+
throw new Error(`Attempted to read unexpected file: ${p}`);
86+
});
87+
const result = readDotnetToolsManifest('/custom/path/file.json');
88+
expect(result).toEqual({
89+
version: 1,
90+
isRoot: true,
91+
tools: {},
92+
});
93+
});
94+
});
95+
96+
describe('readInstalledDotnetToolVersion', () => {
97+
beforeEach(() => {
98+
existsSyncMock.mockReturnValue(true);
99+
readJsonFileMock.mockImplementation((p: string): object => {
100+
if (p === `${root}/.config/dotnet-tools.json`) {
101+
return {
102+
version: 1,
103+
isRoot: true,
104+
tools: {
105+
'swashbuckle.aspnetcore.cli': {
106+
version: '99.99.99',
107+
commands: ['swagger'],
108+
},
109+
},
110+
};
111+
}
112+
throw new Error(`Attempted to read unexpected file: ${p}`);
113+
});
114+
});
115+
116+
it('should read version', async () => {
117+
const result = readInstalledDotnetToolVersion(
118+
'swashbuckle.aspnetcore.cli',
119+
);
120+
expect(result).toEqual('99.99.99');
121+
});
122+
123+
it('should read version if tool case mismatch', async () => {
124+
const result = readInstalledDotnetToolVersion(
125+
'SwashBuckle.AspNetCore.Cli',
126+
);
127+
expect(result).toEqual('99.99.99');
128+
});
129+
130+
it('should return undefined if tool not installed', async () => {
131+
const result = readInstalledDotnetToolVersion('Not.There');
132+
expect(result).toBeUndefined();
133+
});
134+
135+
it('should return undefined if no tool manifest file', async () => {
136+
existsSyncMock.mockReturnValue(false);
137+
const result = readInstalledDotnetToolVersion(
138+
'swashbuckle.aspnetcore.cli',
139+
);
140+
expect(result).toBeUndefined();
141+
});
142+
});
143+
});

0 commit comments

Comments
 (0)