kibana/scripts/plugin_check.js
Clint Andrew Hall 2f92ce1d4a
[dx] Dependency check script for plugins (#171483)
## Summary

Dealing with circular dependencies between plugins has become a sharp
pain point for anyone developing plugins in Kibana.

### Providing dependencies to a plugin

First, a plugin defines its dependencies in its `kibana.jsonc` file as
one of three types:

- `required` - the dependency must be present and enabled -- will be
guaranteed in the lifecycle
- `optional` - the dependency can be missing or disabled -- will be
`undefined` in the lifecycle
- `requiredBundle` - the dependency is required as static code only --
will not be present in the lifecycle

Missing or circular dependencies are detected by the Kibana platform
when it starts.

### Providing dependencies in code

Our plugins are written in and type-checked by Typescript. As such, each
plugin needs to maintain Typescript types defining what the platform is
providing. This is done manually, and there is no enforcement mechanism
between that and the plugin Typescript types. If these dependency
definitions are inconsistent or stale, it can lead to host of issues:

- optional plugins typed as required that are disabled crash the plugin
at runtime;
- plugins that are no longer used still included in dependency checks;
- plugins marked as required or optional that are actually required
bundles.
- etc.

### Dependencies with side-effects

One of the interesting things that has come out of this has been
identifying plugins that provide dependent logic through side-effects,
rather than lifecycles.

As an example, `licensing` provides a lifecycle contracts, but also a
[route handler
context](https://github.com/elastic/kibana/blob/main/x-pack/plugins/licensing/server/licensing_route_handler_context.ts)
as middleware for a dependent plugin. Unfortunately, while this
dependency can be stated as `required` in a dependent plugin's
`kibana.jsonc`, the fact that this is a side-effect makes it incredible
difficult to understand the dependency without searching the code.

<img width="735" alt="Screenshot 2023-12-13 at 10 08 00 AM"
src="b4201c86-4811-4506-b2d0-be5bf8c372b0">

So the side-effect is more or less hidden from developers. This is
likely why we see other plugins using the lifecycle
[logic](https://github.com/elastic/kibana/blob/main/src/plugins/maps_ems/public/kibana_services.ts#L33-L37),
or copy-pasting licensing check code
[[1](https://github.com/elastic/kibana/blob/main/x-pack/plugins/actions/server/lib/license_state.ts),
[2](https://github.com/elastic/kibana/blob/main/x-pack/plugins/alerting/server/lib/license_state.ts)],
or relying on the route context side-effect.

## Proposed (initial) solution

This script is an initial attempt to both identify these problems and
surface a plugin's dependencies in a useful way. In addition, the script
will warn if the classes aren't typed well, not typed at all, or even
don't extend the `core` `Plugin` class.

<img width="1426" alt="Screenshot 2023-12-13 at 12 37 25 AM"
src="e044afb7-26f5-4d96-92db-d2eb0a3dfc6e">
<img width="1413" alt="Screenshot 2023-12-13 at 12 38 07 AM"
src="69217a34-9840-4d32-98de-eeeb863d4a50">
<img width="1071" alt="Screenshot 2023-12-13 at 12 38 35 AM"
src="57736027-2d10-44bf-8230-29fdb8b77cb2">

For side-effects, identifying them is key, and then refactoring the
plugins to provide appropriate logic in the `start` or `setup`
contracts.

## Next steps

- [x] refine the logic
- [ ] write tests
- [ ] add `--fix` option

I'm also considering (in another PR), outputting a consistent type
definition file-- perhaps `kibana.d.ts`-- to the plugin from which the
implementing classes could `Omit<>` or `Pick<>` the relevant contracts.
2024-01-17 17:19:41 -05:00

10 lines
439 B
JavaScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
require('../src/setup_node_env');
require('@kbn/plugin-check').runPluginCheckCli();