Keep multiple installed versions of each plugin

Summary:
This diff changes directory structure for installed plugins to allow installation of multiple versions simultaneously, e.g. to to allow downloading new plugin version while user is still using the previous one, and to have possibility of fast rollback to the previous installed if necessary. The new folder for installed plugins is located in `~/.flipper/installed-plugins` and has the following structure:

  flipper-plugin-reactotron
    1.0.0
      ...
      package.json
    1.0.1
      ...
      package.json
  flipper-plugin-network
    0.67.1
      ...
      package.json
    0.67.2
      ...
      package.json

The tricky part here is that we also need to migrate already installed plugins from the old folder `~/.flipper/thirdparty` to the new folder and maintain the new structure for them.

Another tricky part is that we need to periodically cleanup old versions. For now we will just keep 2 versions of each plugin. Cleanup is performed in background right after Flipper startup.

Reviewed By: mweststrate

Differential Revision: D25393474

fbshipit-source-id: 26617ac26114148f797cc3d6765a42242edc205e
This commit is contained in:
Anton Nikolaev
2020-12-15 09:28:58 -08:00
committed by Facebook GitHub Bot
parent 9c5f59e109
commit 02d695cb28
15 changed files with 194 additions and 398 deletions

View File

@@ -8,19 +8,21 @@
*/
import PluginDetails from './PluginDetails';
import {getInstalledPlugins} from './getInstalledPlugins';
import {getInstalledPlugins} from './pluginInstaller';
import semver from 'semver';
import {getNpmHostedPlugins, NpmPackageDescriptor} from './getNpmHostedPlugins';
import NpmApi from 'npm-api';
import {getPluginDetails} from './getPluginDetails';
import {getPluginInstallationDir} from './pluginPaths';
import {
getPluginDetails,
getPluginDetailsFromPackageJson,
} from './getPluginDetails';
import {getPluginVersionInstallationDir} from './pluginPaths';
import pmap from 'p-map';
import {notNull} from './typeUtils';
const npmApi = new NpmApi();
export type UpdateResult =
| {kind: 'not-installed'; version: string}
| {kind: 'pending'}
| {kind: 'up-to-date'}
| {kind: 'error'; error: Error}
| {kind: 'update-available'; version: string};
@@ -50,7 +52,10 @@ export async function getUpdatablePlugins(
) {
const pkg = await npmApi.repo(npmPackageDescriptor.name).package();
const npmPluginDetails = await getPluginDetails(
getPluginInstallationDir(npmPackageDescriptor.name),
getPluginVersionInstallationDir(
npmPackageDescriptor.name,
npmPackageDescriptor.version,
),
pkg,
);
return {
@@ -62,10 +67,7 @@ export async function getUpdatablePlugins(
};
}
}
const updateStatus: UpdateResult =
installedPlugin.installationStatus === 'installed'
? {kind: 'up-to-date'}
: {kind: 'pending'};
const updateStatus: UpdateResult = {kind: 'up-to-date'};
return {
...installedPlugin,
updateStatus,
@@ -89,10 +91,7 @@ export async function getUpdatablePlugins(
async (notInstalledPlugin) => {
try {
const pkg = await npmApi.repo(notInstalledPlugin.name).package();
const npmPluginDetails = await getPluginDetails(
getPluginInstallationDir(notInstalledPlugin.name),
pkg,
);
const npmPluginDetails = await getPluginDetailsFromPackageJson(pkg);
if (npmPluginDetails.specVersion === 1) {
return null;
}
@@ -106,6 +105,7 @@ export async function getUpdatablePlugins(
} catch (error) {
console.log(
`Failed to load details from npm for plugin ${notInstalledPlugin.name}`,
error,
);
return null;
}