@@ -37,6 +37,54 @@ function isDotNetSdkInstalled(): boolean {
37
37
}
38
38
}
39
39
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 ( / r a p i c g e n \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
+
40
88
/**
41
89
* Checks if the Rapicgen .NET tool is installed globally
42
90
* @returns true if the tool is installed, false otherwise
@@ -56,23 +104,71 @@ function isRapicgenInstalled(): boolean {
56
104
}
57
105
58
106
/**
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
60
142
* @param context The extension context
61
- * @returns true if installation was successful
143
+ * @returns true if installation/update was successful
62
144
*/
63
145
async function installRapicgen ( context : vscode . ExtensionContext ) : Promise < boolean > {
64
146
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
+
65
162
const installResult = await vscode . window . withProgress ( {
66
163
location : vscode . ProgressLocation . Notification ,
67
- title : "Installing Rapicgen tool..." ,
164
+ title : title ,
68
165
cancellable : false
69
166
} , async ( ) => {
70
167
try {
71
- // Get the extension version to match the tool version
72
- const version = getExtensionVersion ( context ) ;
73
-
74
168
// 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' ;
76
172
77
173
// Only specify version if we're in a release build (detected by version from package.json)
78
174
// Local development builds won't specify a version
@@ -83,7 +179,7 @@ async function installRapicgen(context: vscode.ExtensionContext): Promise<boolea
83
179
execSync ( command , { encoding : 'utf-8' } ) ;
84
180
return true ;
85
181
} catch ( error ) {
86
- console . error ( 'Failed to install rapicgen tool:' , error ) ;
182
+ console . error ( 'Failed to install/update rapicgen tool:' , error ) ;
87
183
return false ;
88
184
}
89
185
} ) ;
@@ -95,6 +191,49 @@ async function installRapicgen(context: vscode.ExtensionContext): Promise<boolea
95
191
}
96
192
}
97
193
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
+
98
237
/**
99
238
* Gets the namespace to use for generated code
100
239
* @returns The namespace from configuration or a default value
@@ -199,22 +338,10 @@ async function executeRapicgen(generator: string, specificationFilePath: string,
199
338
return ;
200
339
}
201
340
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 ;
218
345
}
219
346
220
347
const namespace = getNamespace ( ) ;
@@ -303,22 +430,10 @@ async function executeRapicgenTypeScript(generator: string, specificationFilePat
303
430
return ;
304
431
}
305
432
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 ;
322
437
}
323
438
324
439
// For TypeScript, we get an output directory rather than a single file
0 commit comments