@@ -8,12 +8,12 @@ import { ManifestPackage, ManifestPublish } from './manifest';
88import { readVSIXPackage } from './zip' ;
99import { validatePublisher } from './validation' ;
1010import { GalleryApi } from 'azure-devops-node-api/GalleryApi' ;
11- import FormData from 'form-data' ;
1211import { basename , join } from 'path' ;
1312import { tmpdir } from 'os' ;
1413import { IterableBackoff , handleWhen , retry } from 'cockatiel' ;
1514import { getAzureCredentialAccessToken } from './auth' ;
1615import { getOIDCCredential } from './oidc' ;
16+ import { createMultipartStream , runWithStreamError } from './multipart' ;
1717
1818async function withTemporaryPackage < T > ( fn : ( packagePath : string ) => Promise < T > ) : Promise < T > {
1919 const directory = await fs . promises . mkdtemp ( join ( tmpdir ( ) , 'vsce-' ) ) ;
@@ -213,7 +213,6 @@ export interface IInternalPublishOptions {
213213async function _publish ( packagePath : string , sigzipPath : string | undefined , manifest : ManifestPublish , options : IInternalPublishOptions ) {
214214 const pat = await getPAT ( manifest . publisher , options ) ;
215215 const api = await getGalleryAPI ( pat ) ;
216- const packageStream = fs . createReadStream ( packagePath ) ;
217216 const name = `${ manifest . publisher } .${ manifest . name } ` ;
218217 const description = options . target
219218 ? `${ name } (${ options . target } ) v${ manifest . version } `
@@ -254,10 +253,10 @@ async function _publish(packagePath: string, sigzipPath: string | undefined, man
254253 }
255254
256255 if ( sigzipPath ) {
257- await _publishSignedPackage ( api , basename ( packagePath ) , packageStream , basename ( sigzipPath ) , fs . createReadStream ( sigzipPath ) , manifest ) ;
256+ await _publishSignedPackage ( api , packagePath , sigzipPath , manifest ) ;
258257 } else {
259258 try {
260- await api . updateExtension ( undefined , packageStream , manifest . publisher , manifest . name ) ;
259+ await api . updateExtension ( undefined , fs . createReadStream ( packagePath ) , manifest . publisher , manifest . name ) ;
261260 } catch ( err : any ) {
262261 if ( err . statusCode === 409 ) {
263262 if ( options . skipDuplicate ) {
@@ -273,9 +272,9 @@ async function _publish(packagePath: string, sigzipPath: string | undefined, man
273272 }
274273 } else {
275274 if ( sigzipPath ) {
276- await _publishSignedPackage ( api , basename ( packagePath ) , packageStream , basename ( sigzipPath ) , fs . createReadStream ( sigzipPath ) , manifest ) ;
275+ await _publishSignedPackage ( api , packagePath , sigzipPath , manifest ) ;
277276 } else {
278- await api . createExtension ( undefined , packageStream ) ;
277+ await api . createExtension ( undefined , fs . createReadStream ( packagePath ) ) ;
279278 }
280279 }
281280 } catch ( err : any ) {
@@ -295,25 +294,34 @@ async function _publish(packagePath: string, sigzipPath: string | undefined, man
295294 log . done ( `Published ${ description } .` ) ;
296295}
297296
298- async function _publishSignedPackage ( api : GalleryApi , packageName : string , packageStream : fs . ReadStream , sigzipName : string , sigzipStream : fs . ReadStream , manifest : ManifestPublish ) {
297+ async function _publishSignedPackage ( api : GalleryApi , packagePath : string , sigzipPath : string , manifest : ManifestPublish ) {
299298 const extensionType = 'Visual Studio Code' ;
300- const form = new FormData ( ) ;
301- const lineBreak = '\r\n' ;
302- form . setBoundary ( '0f411892-ef48-488f-89d3-4f0546e84723' ) ;
303- form . append ( 'vsix' , packageStream , {
304- header : `--${ form . getBoundary ( ) } ${ lineBreak } Content-Disposition: attachment; name=vsix; filename=\"${ packageName } \"${ lineBreak } Content-Type: application/octet-stream${ lineBreak } ${ lineBreak } `
305- } ) ;
306- form . append ( 'sigzip' , sigzipStream , {
307- header : `--${ form . getBoundary ( ) } ${ lineBreak } Content-Disposition: attachment; name=sigzip; filename=\"${ sigzipName } \"${ lineBreak } Content-Type: application/octet-stream${ lineBreak } ${ lineBreak } `
308- } ) ;
309299
310300 const publishWithRetry = retry ( handleWhen ( err => err . message . includes ( 'timeout' ) ) , {
311301 maxAttempts : 3 ,
312302 backoff : new IterableBackoff ( [ 5_000 , 10_000 , 20_000 ] )
313303 } ) ;
314304
315305 return await publishWithRetry . execute ( async ( ) => {
316- return await api . publishExtensionWithPublisherSignature ( undefined , form , manifest . publisher , manifest . name , extensionType ) ;
306+ const form = createMultipartStream (
307+ [
308+ { name : 'vsix' , filename : basename ( packagePath ) , stream : fs . createReadStream ( packagePath ) } ,
309+ { name : 'sigzip' , filename : basename ( sigzipPath ) , stream : fs . createReadStream ( sigzipPath ) } ,
310+ ] ,
311+ '0f411892-ef48-488f-89d3-4f0546e84723'
312+ ) ;
313+
314+ try {
315+ return await runWithStreamError ( form , ( ) =>
316+ api . publishExtensionWithPublisherSignature ( undefined , form , manifest . publisher , manifest . name , extensionType )
317+ ) ;
318+ } finally {
319+ // Release the file handles when the request did not consume the whole form, which
320+ // otherwise keeps the package locked until the process exits.
321+ if ( ! form . readableEnded ) {
322+ form . destroy ( ) ;
323+ }
324+ }
317325 } ) ;
318326}
319327
0 commit comments