@@ -153,17 +153,33 @@ function _validateGitHubReleaseAssets(githubReleaseDetails, issueMessages) {
153153 return extensionZipAsset ;
154154}
155155
156- async function _validateExtensionPackageJson ( githubReleaseTag , packageJSON , repoDetails , issueMessages ) {
156+ async function _getRegistryPkgJSON ( githubReleaseTag , extensionName ) {
157157 const queryObj = { } ;
158- const newOwner = `github:${ githubReleaseTag . owner } ` ;
159158 const releaseRef = `${ githubReleaseTag . owner } /${ githubReleaseTag . repo } /${ githubReleaseTag . tag } ` ;
160- queryObj [ FIELD_EXTENSION_ID ] = packageJSON . name ;
159+ queryObj [ FIELD_EXTENSION_ID ] = extensionName ;
161160 let registryPKGJSON = await db . getFromIndex ( EXTENSIONS_DETAILS_TABLE , queryObj ) ;
162161 if ( ! registryPKGJSON . isSuccess ) {
163162 // unexpected error
164163 throw new Error ( "Error getting extensionPKG details from db: " + releaseRef ) ;
165164 }
166- registryPKGJSON = registryPKGJSON . documents . length === 1 ? registryPKGJSON . documents [ 0 ] : null ;
165+ if ( registryPKGJSON . documents . length === 1 ) {
166+ let existingRegistryDocumentId = registryPKGJSON . documents [ 0 ] . documentId ;
167+ delete registryPKGJSON . documents [ 0 ] . documentId ;
168+ return {
169+ existingRegistryDocumentId,
170+ registryPKGJSON : registryPKGJSON . documents [ 0 ]
171+ } ;
172+ }
173+ return {
174+ existingRegistryDocumentId : null ,
175+ registryPKGJSON : null
176+ } ;
177+ }
178+
179+ async function _validateExtensionPackageJson ( githubReleaseTag , packageJSON , repoDetails , issueMessages ) {
180+ const newOwner = `github:${ githubReleaseTag . owner } ` ;
181+ let existingRegistryPKGVersion = null ;
182+ let { registryPKGJSON, existingRegistryDocumentId} = await _getRegistryPkgJSON ( githubReleaseTag , packageJSON . name ) ;
167183 let error = "" ;
168184 if ( registryPKGJSON && registryPKGJSON . owner !== newOwner ) {
169185 let errorMsg = `Extension of the same name "${ packageJSON . name } " already exists (owned by https://github.com/${ registryPKGJSON . owner . split ( ":" ) [ 1 ] } ). Please choose a different extension name.` ;
@@ -188,8 +204,9 @@ async function _validateExtensionPackageJson(githubReleaseTag, packageJSON, repo
188204 break ;
189205 }
190206 }
191- if ( lte ( packageJSON . version , registryPKGJSON . metadata . version ) ) {
192- let errorMsg = `Package version should be greater than ${ registryPKGJSON . metadata . version } , but received "${ packageJSON . version } ".` ;
207+ existingRegistryPKGVersion = registryPKGJSON . metadata . version ;
208+ if ( lte ( packageJSON . version , existingRegistryPKGVersion ) ) {
209+ let errorMsg = `Package version should be greater than ${ existingRegistryPKGVersion } , but received "${ packageJSON . version } ".` ;
193210 error = error + `\n${ errorMsg } ` ;
194211 issueMessages . push ( errorMsg ) ;
195212 }
@@ -222,7 +239,7 @@ async function _validateExtensionPackageJson(githubReleaseTag, packageJSON, repo
222239 "downloads" : 0
223240 } ) ;
224241
225- return registryPKGJSON ;
242+ return { existingRegistryPKGVersion , existingRegistryDocumentId , registryPKGJSON} ;
226243}
227244
228245async function _downloadAndValidateExtensionZip ( githubReleaseTag , extensionZipAsset , repoDetails , issueMessages ) {
@@ -252,8 +269,9 @@ async function _downloadAndValidateExtensionZip(githubReleaseTag, extensionZipAs
252269 updatePublishErrors : true ,
253270 error} ;
254271 }
255- const registryPKGJSON = await _validateExtensionPackageJson ( githubReleaseTag , packageJSON , repoDetails , issueMessages ) ;
256- return { extensionZipPath, registryPKGJSON} ;
272+ const { existingRegistryPKGVersion, registryPKGJSON} =
273+ await _validateExtensionPackageJson ( githubReleaseTag , packageJSON , repoDetails , issueMessages ) ;
274+ return { extensionZipPath, existingRegistryPKGVersion, registryPKGJSON} ;
257275}
258276
259277async function _createGithubIssue ( release ) {
@@ -301,11 +319,11 @@ async function _UpdateReleaseInfo(release, existingReleaseInfo) {
301319 if ( ! existingReleaseInfo . githubIssue ) {
302320 existingReleaseInfo . githubIssue = await _createGithubIssue ( release ) ;
303321 }
304- await db . update ( RELEASE_DETAILS_TABLE , existingReleaseInfo . documentId ,
305- existingReleaseInfo ) ;
322+ console . log ( "Update release table: " , await db . update ( RELEASE_DETAILS_TABLE , existingReleaseInfo . documentId ,
323+ existingReleaseInfo ) ) ;
306324 return existingReleaseInfo ;
307325 } else {
308- console . log ( `updating new release ${ releaseRef } details: ` ) ;
326+ console . log ( `creating new release ${ releaseRef } details: ` ) ;
309327 let releaseInfo = {
310328 errors : [ ] ,
311329 status : RELEASE_STATUS_PROCESSING ,
@@ -324,11 +342,37 @@ async function _UpdateReleaseInfo(release, existingReleaseInfo) {
324342 }
325343}
326344
345+ async function _updateRegistryJSONinDB ( existingRegistryPKGVersion , existingRegistryDocumentId , registryPKGJSON ,
346+ issueMessages ) {
347+ let status ;
348+ registryPKGJSON . syncPending = true ;
349+ registryPKGJSON . EXTENSION_ID = registryPKGJSON . metadata . name ;
350+ if ( existingRegistryDocumentId ) {
351+ // we need to update existing extension release only if no one updated the release while this release
352+ // was being published, so the conditional update with version check.
353+ console . log ( "updating extension" , registryPKGJSON . EXTENSION_ID ) ;
354+ status = await db . update ( EXTENSIONS_DETAILS_TABLE , existingRegistryDocumentId ,
355+ registryPKGJSON , `$.metadata.version='${ existingRegistryPKGVersion } '` ) ;
356+ } else {
357+ console . log ( "Creating extension" , registryPKGJSON . EXTENSION_ID ) ;
358+ status = await db . put ( EXTENSIONS_DETAILS_TABLE , registryPKGJSON ) ;
359+ }
360+ if ( ! status . isSuccess ) {
361+ console . error ( `Error putting/updating extension details(did another release happen while releasing this version) in db ${ EXTENSIONS_DETAILS_TABLE } :` +
362+ ` documentId: ${ existingRegistryDocumentId } existing version: ${ existingRegistryPKGVersion } , new pkg: ${ JSON . stringify ( registryPKGJSON ) } ` , status ) ;
363+ const message = `Release failed. Did another release happen for the same extension? ${ registryPKGJSON . metadata . name } ` +
364+ ` while this release was being published?\n If so you may have to update your version number and make a new release with higher version number.` ;
365+ issueMessages . push ( message ) ;
366+ throw { status : HTTP_STATUS_CODES . CONFLICT ,
367+ error : message } ;
368+ }
369+ }
370+
327371export async function publishGithubRelease ( request , reply ) {
328372 let issueMessages = [ ] ,
329373 existingReleaseInfo = null ,
330374 githubReleaseTag = null ,
331- extensionZipPath = null ;
375+ _extensionZipPath = null ;
332376 try {
333377 // releaseRef of the form <org>/<repo>:refs/tags/<dfg>
334378 githubReleaseTag = _validateAndGetParams ( request . query . releaseRef ) ;
@@ -343,19 +387,20 @@ export async function publishGithubRelease(request, reply) {
343387 error : `Draft or PreRelease builds cannot be published.` } ;
344388 }
345389 const extensionZipAsset = _validateGitHubReleaseAssets ( newGithubReleaseDetails , issueMessages ) ;
346- const { extensionZipPath, registryPKGJSON} =
390+ const { extensionZipPath, existingRegistryPKGVersion , existingRegistryDocumentId , registryPKGJSON} =
347391 await _downloadAndValidateExtensionZip ( githubReleaseTag , extensionZipAsset , repoDetails , issueMessages ) ;
392+ _extensionZipPath = extensionZipPath ;
348393 // we should also in the future do a virus scan, but will rely on av in users machine for the time being
349394 // https://developers.virustotal.com/reference/files-scan by Google Cloud is available for non-commercial apps.
350395
351396 await S3 . uploadFile ( EXTENSIONS_BUCKET ,
352397 `extensions/${ registryPKGJSON . metadata . name } -${ registryPKGJSON . metadata . version } .zip` ,
353- extensionZipPath ) ;
398+ _extensionZipPath ) ;
399+ fs . unlink ( _extensionZipPath , console . error ) ; // cleanup downloads. (But we don't check the result)
354400
355- // cleanup
356- setTimeout ( ( ) => {
357- fs . unlink ( extensionZipPath , console . error ) ; // cleanup after we return. (But we don't check the result)
358- } , 1000 ) ;
401+ // publish new package json to registry db
402+ await _updateRegistryJSONinDB ( existingRegistryPKGVersion , existingRegistryDocumentId , registryPKGJSON ,
403+ issueMessages ) ;
359404
360405 const response = {
361406 message : "done"
@@ -366,8 +411,8 @@ export async function publishGithubRelease(request, reply) {
366411 if ( err . updatePublishErrors ) {
367412 _updatePublishErrors ( githubReleaseTag , issueMessages ) ; // dont await, background task
368413 }
369- if ( extensionZipPath ) {
370- fs . unlink ( extensionZipPath , console . error ) ; // cleanup after we return . (But we don't check the result)
414+ if ( _extensionZipPath ) {
415+ fs . unlink ( _extensionZipPath , console . error ) ; // cleanup after an exception . (But we don't check the result)
371416 }
372417 if ( err . status ) {
373418 reply . status ( err . status ) ;
0 commit comments