@@ -46,7 +46,7 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
4646 protected readonly uninstallationManager : PluginUninstallationManager ;
4747
4848 private readonly deployedLocations = new Map < PluginIdentifiers . VersionedId , Set < string > > ( ) ;
49- protected readonly originalLocations = new Map < PluginIdentifiers . VersionedId , string > ( ) ;
49+ protected readonly sourceLocations = new Map < PluginIdentifiers . VersionedId , Set < string > > ( ) ;
5050
5151 /**
5252 * Managed plugin metadata backend entries.
@@ -80,7 +80,7 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
8080 const matches : DeployedPlugin [ ] = [ ] ;
8181 const handle = ( plugins : Iterable < DeployedPlugin > ) : void => {
8282 for ( const plugin of plugins ) {
83- if ( PluginIdentifiers . componentsToVersionWithId ( plugin . metadata . model ) . version === pluginId ) {
83+ if ( PluginIdentifiers . componentsToVersionWithId ( plugin . metadata . model ) . id === pluginId ) {
8484 matches . push ( plugin ) ;
8585 }
8686 }
@@ -117,28 +117,33 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
117117 }
118118 }
119119
120- async deployFrontendPlugins ( frontendPlugins : PluginDeployerEntry [ ] ) : Promise < void > {
120+ async deployFrontendPlugins ( frontendPlugins : PluginDeployerEntry [ ] ) : Promise < number > {
121+ let successes = 0 ;
121122 for ( const plugin of frontendPlugins ) {
122- await this . deployPlugin ( plugin , 'frontend' ) ;
123+ if ( await this . deployPlugin ( plugin , 'frontend' ) ) { successes ++ ; }
123124 }
124125 // resolve on first deploy
125126 this . frontendPluginsMetadataDeferred . resolve ( undefined ) ;
127+ return successes ;
126128 }
127129
128- async deployBackendPlugins ( backendPlugins : PluginDeployerEntry [ ] ) : Promise < void > {
130+ async deployBackendPlugins ( backendPlugins : PluginDeployerEntry [ ] ) : Promise < number > {
131+ let successes = 0 ;
129132 for ( const plugin of backendPlugins ) {
130- await this . deployPlugin ( plugin , 'backend' ) ;
133+ if ( await this . deployPlugin ( plugin , 'backend' ) ) { successes ++ ; }
131134 }
132135 // rebuild translation config after deployment
133136 this . localizationService . buildTranslationConfig ( [ ...this . deployedBackendPlugins . values ( ) ] ) ;
134137 // resolve on first deploy
135138 this . backendPluginsMetadataDeferred . resolve ( undefined ) ;
139+ return successes ;
136140 }
137141
138142 /**
139- * @throws never! in order to isolate plugin deployment
143+ * @throws never! in order to isolate plugin deployment.
144+ * @returns whether the plugin is deployed after running this function. If the plugin was already installed, will still return `true`.
140145 */
141- protected async deployPlugin ( entry : PluginDeployerEntry , entryPoint : keyof PluginEntryPoint ) : Promise < void > {
146+ protected async deployPlugin ( entry : PluginDeployerEntry , entryPoint : keyof PluginEntryPoint ) : Promise < boolean > {
142147 const pluginPath = entry . path ( ) ;
143148 const deployPlugin = this . stopwatch . start ( 'deployPlugin' ) ;
144149 let id ;
@@ -147,23 +152,23 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
147152 const manifest = await this . reader . readPackage ( pluginPath ) ;
148153 if ( ! manifest ) {
149154 deployPlugin . error ( `Failed to read ${ entryPoint } plugin manifest from '${ pluginPath } ''` ) ;
150- return ;
155+ return success = false ;
151156 }
152157
153158 const metadata = this . reader . readMetadata ( manifest ) ;
154159 metadata . isUnderDevelopment = entry . getValue ( 'isUnderDevelopment' ) ?? false ;
155160
156161 id = PluginIdentifiers . componentsToVersionedId ( metadata . model ) ;
157162
158- const deployedLocations = this . deployedLocations . get ( id ) || new Set < string > ( ) ;
163+ const deployedLocations = this . deployedLocations . get ( id ) ?? new Set < string > ( ) ;
159164 deployedLocations . add ( entry . rootPath ) ;
160165 this . deployedLocations . set ( id , deployedLocations ) ;
161- this . originalLocations . set ( id , entry . originalPath ( ) ) ;
166+ this . setSourceLocationsForPlugin ( id , entry ) ;
162167
163168 const deployedPlugins = entryPoint === 'backend' ? this . deployedBackendPlugins : this . deployedFrontendPlugins ;
164169 if ( deployedPlugins . has ( id ) ) {
165170 deployPlugin . debug ( `Skipped ${ entryPoint } plugin ${ metadata . model . name } already deployed` ) ;
166- return ;
171+ return true ;
167172 }
168173
169174 const { type } = entry ;
@@ -173,23 +178,25 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
173178 deployedPlugins . set ( id , deployed ) ;
174179 deployPlugin . log ( `Deployed ${ entryPoint } plugin "${ id } " from "${ metadata . model . entryPoint [ entryPoint ] || pluginPath } "` ) ;
175180 } catch ( e ) {
176- success = false ;
177181 deployPlugin . error ( `Failed to deploy ${ entryPoint } plugin from '${ pluginPath } ' path` , e ) ;
182+ return success = false ;
178183 } finally {
179184 if ( success && id ) {
180- this . uninstallationManager . markAsInstalled ( id ) ;
185+ this . markAsInstalled ( id ) ;
181186 }
182187 }
188+ return success ;
183189 }
184190
185191 async uninstallPlugin ( pluginId : PluginIdentifiers . VersionedId ) : Promise < boolean > {
186192 try {
187- const originalPath = this . originalLocations . get ( pluginId ) ;
188- if ( ! originalPath ) {
193+ const sourceLocations = this . sourceLocations . get ( pluginId ) ;
194+ if ( ! sourceLocations ) {
189195 return false ;
190196 }
191- await fs . remove ( originalPath ) ;
192- this . originalLocations . delete ( pluginId ) ;
197+ await Promise . all ( Array . from ( sourceLocations ,
198+ location => fs . remove ( location ) . catch ( err => console . error ( `Failed to remove source for ${ pluginId } at ${ location } ` , err ) ) ) ) ;
199+ this . sourceLocations . delete ( pluginId ) ;
193200 this . uninstallationManager . markAsUninstalled ( pluginId ) ;
194201 return true ;
195202 } catch ( e ) {
@@ -198,6 +205,26 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
198205 }
199206 }
200207
208+ protected markAsInstalled ( id : PluginIdentifiers . VersionedId ) : void {
209+ const metadata = PluginIdentifiers . idAndVersionFromVersionedId ( id ) ;
210+ if ( metadata ) {
211+ const toMarkAsUninstalled : PluginIdentifiers . VersionedId [ ] = [ ] ;
212+ const checkForDifferentVersions = ( others : Iterable < PluginIdentifiers . VersionedId > ) => {
213+ for ( const other of others ) {
214+ const otherMetadata = PluginIdentifiers . idAndVersionFromVersionedId ( other ) ;
215+ if ( metadata . id === otherMetadata ?. id && metadata . version !== otherMetadata . version ) {
216+ toMarkAsUninstalled . push ( other ) ;
217+ }
218+ }
219+ } ;
220+ checkForDifferentVersions ( this . deployedFrontendPlugins . keys ( ) ) ;
221+ checkForDifferentVersions ( this . deployedBackendPlugins . keys ( ) ) ;
222+ this . uninstallationManager . markAsUninstalled ( ...toMarkAsUninstalled ) ;
223+ this . uninstallationManager . markAsInstalled ( id ) ;
224+ toMarkAsUninstalled . forEach ( pluginToUninstall => this . uninstallPlugin ( pluginToUninstall ) ) ;
225+ }
226+ }
227+
201228 async undeployPlugin ( pluginId : PluginIdentifiers . VersionedId ) : Promise < boolean > {
202229 this . deployedBackendPlugins . delete ( pluginId ) ;
203230 this . deployedFrontendPlugins . delete ( pluginId ) ;
@@ -220,4 +247,14 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
220247
221248 return true ;
222249 }
250+
251+ protected setSourceLocationsForPlugin ( id : PluginIdentifiers . VersionedId , entry : PluginDeployerEntry ) : void {
252+ const knownLocations = this . sourceLocations . get ( id ) ?? new Set ( ) ;
253+ const maybeStoredLocations = entry . getValue ( 'sourceLocations' ) ;
254+ const storedLocations = Array . isArray ( maybeStoredLocations ) && maybeStoredLocations . every ( location => typeof location === 'string' )
255+ ? maybeStoredLocations . concat ( entry . originalPath ( ) )
256+ : [ entry . originalPath ( ) ] ;
257+ storedLocations . forEach ( location => knownLocations . add ( location ) ) ;
258+ this . sourceLocations . set ( id , knownLocations ) ;
259+ }
223260}
0 commit comments