Report versions of plugins and plugin load success rate (#1270)

Summary:
Pull Request resolved: https://github.com/facebook/flipper/pull/1270

1) Report versions of loaded plugins so we can track auto-updates
2) Report plugin load success rate

Reviewed By: jknoxville

Differential Revision: D22066598

fbshipit-source-id: 23ef2fb37260438cc1a9a873c88a50b75cd718f4
This commit is contained in:
Anton Nikolaev
2020-06-16 06:43:53 -07:00
committed by Facebook GitHub Bot
parent fec52a3989
commit c995fc8fc2
3 changed files with 74 additions and 25 deletions

View File

@@ -8,6 +8,9 @@
*/ */
jest.mock('../../defaultPlugins'); jest.mock('../../defaultPlugins');
try {
jest.mock('../../fb/Logger', () => require('../../fb-stubs/Logger'));
} catch {}
import dispatcher, { import dispatcher, {
getDynamicPlugins, getDynamicPlugins,
@@ -158,6 +161,7 @@ test('requirePlugin loads plugin', () => {
entry: path.join(__dirname, 'TestPlugin'), entry: path.join(__dirname, 'TestPlugin'),
version: '1.0.0', version: '1.0.0',
}); });
expect(plugin).not.toBeNull();
expect(plugin!.prototype).toBeInstanceOf(FlipperPlugin); expect(plugin!.prototype).toBeInstanceOf(FlipperPlugin);
expect(plugin!.id).toBe(TestPlugin.id); expect(plugin!.id).toBe(TestPlugin.id);
}); });

View File

@@ -33,6 +33,7 @@ import semver from 'semver';
import {PluginDetails} from 'flipper-plugin-lib'; import {PluginDetails} from 'flipper-plugin-lib';
import {addNotification} from '../reducers/notifications'; import {addNotification} from '../reducers/notifications';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import {tryCatchReportPluginFailures, reportUsage} from '../utils/metrics';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import getPluginIndex from '../utils/getDefaultPluginsIndex'; import getPluginIndex from '../utils/getDefaultPluginsIndex';
@@ -58,6 +59,7 @@ export default (store: Store, logger: Logger) => {
const initialPlugins: Array< const initialPlugins: Array<
typeof FlipperPlugin | typeof FlipperDevicePlugin typeof FlipperPlugin | typeof FlipperDevicePlugin
> = filterNewestVersionOfEachPlugin(getBundledPlugins(), getDynamicPlugins()) > = filterNewestVersionOfEachPlugin(getBundledPlugins(), getDynamicPlugins())
.map(reportVersion)
.filter(checkDisabled(disabledPlugins)) .filter(checkDisabled(disabledPlugins))
.filter(checkGK(gatekeepedPlugins)) .filter(checkGK(gatekeepedPlugins))
.map(requirePlugin(failedPlugins, defaultPluginsIndex)) .map(requirePlugin(failedPlugins, defaultPluginsIndex))
@@ -70,7 +72,6 @@ export default (store: Store, logger: Logger) => {
const deprecatedSpecPlugins = initialPlugins.filter( const deprecatedSpecPlugins = initialPlugins.filter(
(p) => !p.isDefault && p.details.specVersion === 1, (p) => !p.isDefault && p.details.specVersion === 1,
); );
for (const plugin of deprecatedSpecPlugins) { for (const plugin of deprecatedSpecPlugins) {
store.dispatch( store.dispatch(
addNotification({ addNotification({
@@ -149,6 +150,17 @@ export default (store: Store, logger: Logger) => {
); );
}; };
function reportVersion(pluginDetails: PluginDetails) {
reportUsage(
'plugin:version',
{
version: pluginDetails.version,
},
pluginDetails.id,
);
return pluginDetails;
}
export function filterNewestVersionOfEachPlugin( export function filterNewestVersionOfEachPlugin(
bundledPlugins: PluginDetails[], bundledPlugins: PluginDetails[],
dynamicPlugins: PluginDetails[], dynamicPlugins: PluginDetails[],
@@ -237,33 +249,43 @@ export const requirePlugin = (
pluginDetails: PluginDetails, pluginDetails: PluginDetails,
): typeof FlipperPlugin | typeof FlipperDevicePlugin | null => { ): typeof FlipperPlugin | typeof FlipperDevicePlugin | null => {
try { try {
let plugin = pluginDetails.isDefault return tryCatchReportPluginFailures(
? defaultPluginsIndex[pluginDetails.name] () => requirePluginInternal(pluginDetails, defaultPluginsIndex, reqFn),
: reqFn(pluginDetails.entry); 'plugin:load',
if (plugin.default) { pluginDetails.id,
plugin = plugin.default; );
}
if (!(plugin.prototype instanceof FlipperBasePlugin)) {
throw new Error(`Plugin ${plugin.name} is not a FlipperBasePlugin`);
}
plugin.id = plugin.id || pluginDetails.id;
plugin.packageName = pluginDetails.name;
plugin.details = pluginDetails;
// set values from package.json as static variables on class
Object.keys(pluginDetails).forEach((key) => {
if (key !== 'name' && key !== 'id') {
plugin[key] =
plugin[key] || pluginDetails[key as keyof PluginDetails];
}
});
return plugin;
} catch (e) { } catch (e) {
failedPlugins.push([pluginDetails, e.message]); failedPlugins.push([pluginDetails, e.message]);
console.error(pluginDetails, e); console.error(`Plugin ${pluginDetails.id} failed to load`, e);
return null; return null;
} }
}; };
}; };
const requirePluginInternal = (
pluginDetails: PluginDetails,
defaultPluginsIndex: any,
reqFn: Function = global.electronRequire,
) => {
let plugin = pluginDetails.isDefault
? defaultPluginsIndex[pluginDetails.name]
: reqFn(pluginDetails.entry);
if (plugin.default) {
plugin = plugin.default;
}
if (!(plugin.prototype instanceof FlipperBasePlugin)) {
throw new Error(`Plugin ${plugin.name} is not a FlipperBasePlugin`);
}
plugin.id = plugin.id || pluginDetails.id;
plugin.packageName = pluginDetails.name;
plugin.details = pluginDetails;
// set values from package.json as static variables on class
Object.keys(pluginDetails).forEach((key) => {
if (key !== 'name' && key !== 'id') {
plugin[key] = plugin[key] || pluginDetails[key as keyof PluginDetails];
}
});
return plugin;
};

View File

@@ -110,6 +110,29 @@ export function tryCatchReportPlatformFailures<T>(
} }
} }
/*
* Wraps a closure, preserving it's functionality but logging the success or
failure state of it.
*/
export function tryCatchReportPluginFailures<T>(
closure: () => T,
name: string,
plugin: string,
): T {
try {
const result = closure();
logPluginSuccessRate(name, plugin, {kind: 'success'});
return result;
} catch (e) {
logPluginSuccessRate(name, plugin, {
kind: 'failure',
supportedOperation: !(e instanceof UnsupportedError),
error: e,
});
throw e;
}
}
/** /**
* Track usage of a feature. * Track usage of a feature.
* @param action Unique name for the action performed. E.g. captureScreenshot * @param action Unique name for the action performed. E.g. captureScreenshot