installPlugin method

Future<PluginOperationResult> installPlugin(
  1. String plugin, [
  2. PluginScope scope = PluginScope.user
])

Install a plugin (settings-first).

Order of operations:

  1. Look up plugin in marketplaces.
  2. Write settings (declares intent).
  3. Cache plugin and record version hint (materialization).

Implementation

Future<PluginOperationResult> installPlugin(
  String plugin, [
  PluginScope scope = PluginScope.user,
]) async {
  assertInstallableScope(scope);
  final id = parsePluginIdentifier(plugin);

  final entry = await getPluginByIdFn(plugin);
  if (entry == null) {
    final location =
        id.marketplace != null
            ? 'marketplace "${id.marketplace}"'
            : 'any configured marketplace';
    return PluginOperationResult(
      success: false,
      message: 'Plugin "${id.name}" not found in $location',
    );
  }

  final pluginId = '${entry.name}@${id.marketplace ?? 'default'}';

  if (isPluginBlockedByPolicy(pluginId)) {
    return PluginOperationResult(
      success: false,
      message:
          'Plugin "${entry.name}" is blocked by your organization\'s policy '
          'and cannot be installed',
    );
  }

  // Validate the manifest.
  final validation = validateManifest(entry);
  if (!validation.isValid) {
    return PluginOperationResult(
      success: false,
      message:
          'Plugin "${id.name}" has an invalid manifest: '
          '${validation.errors.join('; ')}',
    );
  }

  // Write settings (the intent declaration).
  final settingSource = scopeToSettingSource(scope);
  final current = getSettingsEnabledPlugins(settingSource) ?? {};
  updateSettings(settingSource, {
    'enabledPlugins': {...current, pluginId: true},
  });
  clearAllCaches();

  return PluginOperationResult(
    success: true,
    message:
        'Successfully installed plugin: $pluginId (scope: ${scope.name})',
    pluginId: pluginId,
    pluginName: entry.name,
    scope: scope,
  );
}