Skip to content

Commit 26201e1

Browse files
Merge pull request #1156 from christianhelle/upgrade-rapicgen
2 parents bf8eab4 + 395f999 commit 26201e1

File tree

3 files changed

+159
-44
lines changed

3 files changed

+159
-44
lines changed

src/VSCode/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/VSCode/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "rest-api-client-code-generator",
3-
"displayName": "REST API Client Code Generator",
2+
"name": "apiclientcodegen",
3+
"displayName": "REST API Client Code Generator for VS Code",
44
"description": "Generate REST API client code from OpenAPI/Swagger specifications",
55
"version": "0.1.0",
66
"engines": {

src/VSCode/src/extension.ts

Lines changed: 155 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,54 @@ function isDotNetSdkInstalled(): boolean {
3737
}
3838
}
3939

40+
/**
41+
* Gets the installed Rapicgen .NET tool version
42+
* @returns The installed version as a string, or null if not installed or version cannot be determined
43+
*/
44+
function getInstalledRapicgenVersion(): string | null {
45+
try {
46+
// Different command for Windows vs. Unix platforms
47+
const command = platform() === 'win32'
48+
? 'dotnet tool list --global | findstr rapicgen'
49+
: 'dotnet tool list --global | grep rapicgen';
50+
51+
const output = execSync(command, { encoding: 'utf-8' });
52+
53+
// Parse the version from the command output
54+
// The output format is usually: package-id version commands
55+
const match = output.match(/rapicgen\s+(\d+\.\d+\.\d+)/i);
56+
return match && match[1] ? match[1] : null;
57+
} catch (error) {
58+
console.error('Error getting Rapicgen version:', error);
59+
return null;
60+
}
61+
}
62+
63+
/**
64+
* Compares two version strings
65+
* @param version1 First version string
66+
* @param version2 Second version string
67+
* @returns 1 if version1 > version2, -1 if version1 < version2, 0 if equal
68+
*/
69+
function compareVersions(version1: string, version2: string): number {
70+
const parts1 = version1.split('.').map(Number);
71+
const parts2 = version2.split('.').map(Number);
72+
73+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
74+
const p1 = i < parts1.length ? parts1[i] : 0;
75+
const p2 = i < parts2.length ? parts2[i] : 0;
76+
77+
if (p1 > p2) {
78+
return 1;
79+
}
80+
if (p1 < p2) {
81+
return -1;
82+
}
83+
}
84+
85+
return 0;
86+
}
87+
4088
/**
4189
* Checks if the Rapicgen .NET tool is installed globally
4290
* @returns true if the tool is installed, false otherwise
@@ -56,23 +104,71 @@ function isRapicgenInstalled(): boolean {
56104
}
57105

58106
/**
59-
* Installs the Rapicgen .NET tool globally
107+
* Checks if the Rapicgen tool needs to be updated based on extension version
108+
* @param context The extension context
109+
* @returns An object indicating if update is needed and current/target versions
110+
*/
111+
function checkRapicgenVersionStatus(context: vscode.ExtensionContext): {
112+
needsUpdate: boolean;
113+
currentVersion: string | null;
114+
targetVersion: string | null;
115+
} {
116+
const extensionVersion = getExtensionVersion(context);
117+
const installedVersion = getInstalledRapicgenVersion();
118+
119+
// Default result
120+
const result = {
121+
needsUpdate: false,
122+
currentVersion: installedVersion,
123+
targetVersion: extensionVersion || null
124+
};
125+
126+
// If extension is dev version (0.1.0) or we can't get versions, no update needed
127+
if (!extensionVersion || extensionVersion === '0.1.0' || !installedVersion) {
128+
return result;
129+
}
130+
131+
// Compare versions and determine if update is needed
132+
const comparison = compareVersions(installedVersion, extensionVersion);
133+
134+
// Only update if the installed version is older (comparison < 0)
135+
result.needsUpdate = comparison < 0;
136+
137+
return result;
138+
}
139+
140+
/**
141+
* Installs or updates the Rapicgen .NET tool globally
60142
* @param context The extension context
61-
* @returns true if installation was successful
143+
* @returns true if installation/update was successful
62144
*/
63145
async function installRapicgen(context: vscode.ExtensionContext): Promise<boolean> {
64146
try {
147+
const version = getExtensionVersion(context);
148+
const isUpdate = isRapicgenInstalled();
149+
const versionStatus = isUpdate ? checkRapicgenVersionStatus(context) : null;
150+
151+
// If tool is already installed and up-to-date (or newer), no need to update
152+
if (isUpdate && versionStatus && !versionStatus.needsUpdate) {
153+
console.log(`Rapicgen is already installed with version ${versionStatus.currentVersion}, which is greater than or equal to extension version ${versionStatus.targetVersion}. No update needed.`);
154+
return true;
155+
}
156+
157+
// Set the appropriate title for the progress notification
158+
const title = isUpdate && versionStatus?.needsUpdate
159+
? `Updating Rapicgen tool from v${versionStatus.currentVersion} to v${versionStatus.targetVersion}...`
160+
: "Installing Rapicgen tool...";
161+
65162
const installResult = await vscode.window.withProgress({
66163
location: vscode.ProgressLocation.Notification,
67-
title: "Installing Rapicgen tool...",
164+
title: title,
68165
cancellable: false
69166
}, async () => {
70167
try {
71-
// Get the extension version to match the tool version
72-
const version = getExtensionVersion(context);
73-
74168
// Build the installation command
75-
let command = 'dotnet tool install --global rapicgen';
169+
let command = isUpdate && versionStatus?.needsUpdate
170+
? 'dotnet tool update --global rapicgen'
171+
: 'dotnet tool install --global rapicgen';
76172

77173
// Only specify version if we're in a release build (detected by version from package.json)
78174
// Local development builds won't specify a version
@@ -83,7 +179,7 @@ async function installRapicgen(context: vscode.ExtensionContext): Promise<boolea
83179
execSync(command, { encoding: 'utf-8' });
84180
return true;
85181
} catch (error) {
86-
console.error('Failed to install rapicgen tool:', error);
182+
console.error('Failed to install/update rapicgen tool:', error);
87183
return false;
88184
}
89185
});
@@ -95,6 +191,49 @@ async function installRapicgen(context: vscode.ExtensionContext): Promise<boolea
95191
}
96192
}
97193

194+
/**
195+
* Ensures that the Rapicgen tool is installed and up-to-date
196+
* @param context The extension context
197+
* @returns true if the tool is available and ready to use, false otherwise
198+
*/
199+
async function ensureRapicgenToolAvailable(context: vscode.ExtensionContext): Promise<boolean> {
200+
// Check if the Rapicgen tool is installed
201+
if (!isRapicgenInstalled()) {
202+
const shouldInstall = await vscode.window.showInformationMessage(
203+
'The Rapicgen .NET tool is not installed. Would you like to install it?',
204+
'Yes', 'No'
205+
);
206+
207+
if (shouldInstall === 'Yes') {
208+
const installSuccess = await installRapicgen(context);
209+
if (!installSuccess) {
210+
vscode.window.showErrorMessage('Failed to install the Rapicgen .NET tool. Please install it manually with "dotnet tool install --global rapicgen".');
211+
return false;
212+
}
213+
return true;
214+
} else {
215+
return false;
216+
}
217+
} else {
218+
// Check if update is needed
219+
const versionStatus = checkRapicgenVersionStatus(context);
220+
if (versionStatus.needsUpdate) {
221+
const shouldUpdate = await vscode.window.showInformationMessage(
222+
`A newer version of the Rapicgen tool is available (current: v${versionStatus.currentVersion}, available: v${versionStatus.targetVersion}). Would you like to update?`,
223+
'Yes', 'No'
224+
);
225+
226+
if (shouldUpdate === 'Yes') {
227+
const updateSuccess = await installRapicgen(context);
228+
if (!updateSuccess) {
229+
vscode.window.showWarningMessage(`Failed to update the Rapicgen tool. Continuing with existing version ${versionStatus.currentVersion}.`);
230+
}
231+
}
232+
}
233+
return true;
234+
}
235+
}
236+
98237
/**
99238
* Gets the namespace to use for generated code
100239
* @returns The namespace from configuration or a default value
@@ -199,22 +338,10 @@ async function executeRapicgen(generator: string, specificationFilePath: string,
199338
return;
200339
}
201340

202-
// Check if the Rapicgen tool is installed
203-
if (!isRapicgenInstalled()) {
204-
const shouldInstall = await vscode.window.showInformationMessage(
205-
'The Rapicgen .NET tool is not installed. Would you like to install it?',
206-
'Yes', 'No'
207-
);
208-
209-
if (shouldInstall === 'Yes') {
210-
const installSuccess = await installRapicgen(context);
211-
if (!installSuccess) {
212-
vscode.window.showErrorMessage('Failed to install the Rapicgen .NET tool. Please install it manually with "dotnet tool install --global rapicgen".');
213-
return;
214-
}
215-
} else {
216-
return;
217-
}
341+
// Ensure the Rapicgen tool is installed and up-to-date
342+
const rapicgenAvailable = await ensureRapicgenToolAvailable(context);
343+
if (!rapicgenAvailable) {
344+
return;
218345
}
219346

220347
const namespace = getNamespace();
@@ -303,22 +430,10 @@ async function executeRapicgenTypeScript(generator: string, specificationFilePat
303430
return;
304431
}
305432

306-
// Check if the Rapicgen tool is installed
307-
if (!isRapicgenInstalled()) {
308-
const shouldInstall = await vscode.window.showInformationMessage(
309-
'The Rapicgen .NET tool is not installed. Would you like to install it?',
310-
'Yes', 'No'
311-
);
312-
313-
if (shouldInstall === 'Yes') {
314-
const installSuccess = await installRapicgen(context);
315-
if (!installSuccess) {
316-
vscode.window.showErrorMessage('Failed to install the Rapicgen .NET tool. Please install it manually with "dotnet tool install --global rapicgen".');
317-
return;
318-
}
319-
} else {
320-
return;
321-
}
433+
// Ensure the Rapicgen tool is installed and up-to-date
434+
const rapicgenAvailable = await ensureRapicgenToolAvailable(context);
435+
if (!rapicgenAvailable) {
436+
return;
322437
}
323438

324439
// For TypeScript, we get an output directory rather than a single file

0 commit comments

Comments
 (0)