-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[dx] Dependency check script for plugins #171483
[dx] Dependency check script for plugins #171483
Conversation
a242392
to
0d6543d
Compare
c88967b
to
0935d63
Compare
0935d63
to
d930b1f
Compare
Thanks for your effort @clintandrewhall. Some thoughts: You mention circular dependencies as a pain point for Kibana and the impetus for this PR. Perhaps I'm misunderstanding, but the Kibana dev server will automatically stop at runtime when a circular dependency is found. I suppose my question is, at what point during development (including dev, CI and build) is this script intended to run? The other issue that is noted is keeping the setup and start types of a plugin in sync with its dependencies in kibana.jsonc. This is very promising and would definitely be an improvement in DX. It wouldn't necessarily need the terminal UI part for it to be useful, as long as it automatically generates types in a specific subfolder of the plugin which could then be included in the plugin.ts file. Excited to see where this goes next. |
Thanks @CoenWarmer ! Replies below:
That's correct. But as we discovered building Serverless projects, if the types and
Right now, it's for dev as a sanity check. Depending on what we think of it, I could see it being expanded to run in CI, similar to how we output bundle tables. I also want to auto-fix types to reflect the manifest when we
💯 - I want to do this, specifically, create consistently-named types and put them in Thank you for the feedback! |
…istencies (#173297) > Changes identified using the script from #171483 ## Summary The script from #171483 can identify inconsistencies and untyped dependencies in Kibana plugins. This PR fixes the obvious: - `notifications` - move `actions` and `licensing` to `optionalPlugins`. - `serverless` - move `kibanaReact` to `requiredBundles`. - `serverlessObservability` - delete dead code and `kibanaReact` dependency. - `reporting` - move `esUiShared` and `embeddable` plugins to `requiredBundles`. - `uiActions` - remove `dataViews` dependency, (only a type is being used). - `urlDrilldowns` - move `uiActions` to `requiredBundles`. - Type all plugins using the `Setup` and `Start` generics on the core `Plugin` interface. - Consistently name them. - The exports needed to be named their original names; this will be addressed in follow up work, (to avoid pinging teams) - Add a `_` to unused parameters. ## Remaining Issues ### `licensing` and `licensingApiGuard` Both of these plugins introduce side-effects, rather than dependent logic. These need to be refactored to be consumed instead. <img width="735" alt="Screenshot 2023-12-13 at 10 08 00 AM" src="https://github.com/elastic/kibana/assets/297604/57916ffd-299d-4ca8-b796-dea2d06dca4a"> <img width="740" alt="Screenshot 2023-12-13 at 10 08 08 AM" src="https://github.com/elastic/kibana/assets/297604/a2af254f-adec-4bf9-869a-8acf34c0c9b4"> ## Resolved issues ### `reporting` ~~The `reporting` plugin requires `embeddable` and `esUiShared`, but it's unclear if these still apply, or if they are required for side-effects. Perhaps @tsullivan can help clarify?~~ Both are being used for static code. Moving to `requiredBundles`, and need to follow-up to create packages. <img width="800" alt="Screenshot 2023-12-13 at 10 08 23 AM" src="https://github.com/elastic/kibana/assets/297604/7629fb92-d28e-43de-bfeb-97410cff424e"> ### `uiActions` ~~The `uiActions` plugin requires `dataViews`. We need to determine if this is a side-effect dependency, or a direct dependency, and remove or refactor as necessary.~~ It's only using a type. Removing the dependency entirely, (`requiredBundles` requires actual code be used). <img width="622" alt="Screenshot 2023-12-13 at 10 08 33 AM" src="https://github.com/elastic/kibana/assets/297604/39916f05-dafc-4f42-b5d8-1abcb1267b5b"> ### urlDrilldown ~~The `urlDrilldown` plugin requires `uiActions`. We need to determine if this is a side-effect dependency, or a direct dependency, and remove or refactor as necessary.~~ Static code usage-- moving to `requiredBundles`. <img width="732" alt="Screenshot 2023-12-13 at 10 13 13 AM" src="https://github.com/elastic/kibana/assets/297604/af32f939-f866-483d-8dd0-aab962397dbb"> --------- Co-authored-by: kibanamachine <[email protected]>
b9b8751
to
c6cb45c
Compare
Pinging @elastic/kibana-operations (Team:Operations) |
Pinging @elastic/appex-sharedux (Team:SharedUX) |
Pinging @elastic/obs-ux-infra_services-team (Team:obs-ux-infra_services) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review and I tested all CLI options. For evaluating correctness of the graph and plugin lifecycle, I didn't see any issues but I'm operating at a distance from this conceptually. Approving for operations as it's a development dependency, but if there's anyone else on operations that wants to take a second pass it would be helpful.
c77805a
to
6ace3b1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to x-pack/plugins/apm/scripts/infer_route_return_types/index.ts lgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally with observability and it seems to work well. I've changed some dependencies from required to optional in the plugin type definitions, and they are reported "required" as expected.
To check a single plugin, run the following command from the root of the Kibana repo: | ||
|
||
```sh | ||
node scripts/plugin_check --plugin pluginName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
node scripts/plugin_check --plugin pluginName | |
node scripts/plugin_check --dependencies pluginName |
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Public APIs missing comments
Public APIs missing exports
Unknown metric groupsAPI count
References to deprecated APIs
History
To update your PR or re-run it, just comment with: |
## 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="https://github.com/elastic/kibana/assets/297604/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="https://github.com/elastic/kibana/assets/297604/e044afb7-26f5-4d96-92db-d2eb0a3dfc6e"> <img width="1413" alt="Screenshot 2023-12-13 at 12 38 07 AM" src="https://github.com/elastic/kibana/assets/297604/69217a34-9840-4d32-98de-eeeb863d4a50"> <img width="1071" alt="Screenshot 2023-12-13 at 12 38 35 AM" src="https://github.com/elastic/kibana/assets/297604/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.
## Summary The script from #171483 can identify inconsistencies and untyped dependencies in Kibana plugins. This PR fixes: - Move `esUiShared`, `kibanaReact`, `kibanaUtils`, and `savedObjectsFinder` to `requiredBundles` - Rename `CasesUiSetup` to `CasesPublicSetup` - Rename `CasesUiStart` to `CasesPublicStart` - Rename `CasesSetup` to `CasesServerSetup` - Rename `CasesStart` to `CasesServerStart` - Rename `CasesPluginSetup` (public) to `CasesPublicSetupDependencies` - Rename `CasesPluginStart` (public) to `CasesPublicStartDependencies` - Moved `PluginsSetup` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerSetupDependencies` - Moved `PluginsStart` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerStartDependencies` I could not add the `apm` to optional plugins because I get an error due to circular dependencies. Observability and Secuirty solution have `apm` and `cases` as a dependency and probably this cause the following error. <details> <summary>Error</summary> ``` info Running model version mapping addition checks │ info Generating field lists from registry and file │ info Loading core with all plugins enabled so that we can get all savedObject mappings... │ERROR UNHANDLED ERROR: Error: Topological ordering of plugins did not complete, these plugins have cyclic or missing dependencies: ["aiAssistantManagementObservability","aiops","apm","assetManager","cases","cloudDefend","datasetQuality","elasticAssistant","enterpriseSearch","exploratoryView","infra","kubernetesSecurity","logsShared","logstash","metricsDataAccess","ml","monitoring","observability","observabilityAIAssistant","observabilityOnboarding","observabilityShared","observabilityLogsExplorer","osquery","profiling","securitySolution","securitySolutionEss","securitySolutionServerless","serverlessObservability","sessionView","synthetics","threatIntelligence","timelines","upgradeAssistant","uptime","ux"] │ERROR at PluginsSystem.getTopologicallySortedPluginNames (plugins_system.ts:354:13) │ERROR at PluginsSystem.uiPlugins (plugins_system.ts:274:36) │ERROR at PluginsService.discover (plugins_service.ts:120:58) │ERROR at Server.preboot (server.ts:172:30) │ERROR at Root.preboot (index.ts:57:14) │ERROR at extract_field_lists_from_plugins_worker.ts:51:3 ERROR UNHANDLED ERROR ERROR Error: worker exited without sending mappings at extractFieldListsFromPlugins (extract_field_lists_from_plugins.ts:62:11) at processTicksAndRejections (node:internal/process/task_queues:95:5) at runModelVersionMappingAdditionsChecks (run_versions_mapping_additions_check.ts:28:66) at run_check_mappings_update_cli.ts:23:9 at tooling_log.ts:84:18 at description (run_check_mappings_update_cli.ts:22:7) at run.ts:73:10 at withProcRunner (with_proc_runner.ts:29:5) at run (run.ts:71:5) ``` </details> Also, the `storage` is passed by the `cases` plugin to the Kibana context and is not a dependency to another plugin. The script assumes that the `storage` is a plugin dependency and reports it. Neverthelss, this demonstrates that we should stop passing `storage` to the Kibana context and use hooks like `useLocalStorage`. ## Results from the script <img width="727" alt="Screenshot 2024-02-17 at 8 54 32 AM" src="https://github.com/elastic/kibana/assets/7871006/1c86f501-72c5-473c-92f5-7a4935da914b"> ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <[email protected]>
## Summary The script from elastic#171483 can identify inconsistencies and untyped dependencies in Kibana plugins. This PR fixes: - Move `esUiShared`, `kibanaReact`, `kibanaUtils`, and `savedObjectsFinder` to `requiredBundles` - Rename `CasesUiSetup` to `CasesPublicSetup` - Rename `CasesUiStart` to `CasesPublicStart` - Rename `CasesSetup` to `CasesServerSetup` - Rename `CasesStart` to `CasesServerStart` - Rename `CasesPluginSetup` (public) to `CasesPublicSetupDependencies` - Rename `CasesPluginStart` (public) to `CasesPublicStartDependencies` - Moved `PluginsSetup` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerSetupDependencies` - Moved `PluginsStart` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerStartDependencies` I could not add the `apm` to optional plugins because I get an error due to circular dependencies. Observability and Secuirty solution have `apm` and `cases` as a dependency and probably this cause the following error. <details> <summary>Error</summary> ``` info Running model version mapping addition checks │ info Generating field lists from registry and file │ info Loading core with all plugins enabled so that we can get all savedObject mappings... │ERROR UNHANDLED ERROR: Error: Topological ordering of plugins did not complete, these plugins have cyclic or missing dependencies: ["aiAssistantManagementObservability","aiops","apm","assetManager","cases","cloudDefend","datasetQuality","elasticAssistant","enterpriseSearch","exploratoryView","infra","kubernetesSecurity","logsShared","logstash","metricsDataAccess","ml","monitoring","observability","observabilityAIAssistant","observabilityOnboarding","observabilityShared","observabilityLogsExplorer","osquery","profiling","securitySolution","securitySolutionEss","securitySolutionServerless","serverlessObservability","sessionView","synthetics","threatIntelligence","timelines","upgradeAssistant","uptime","ux"] │ERROR at PluginsSystem.getTopologicallySortedPluginNames (plugins_system.ts:354:13) │ERROR at PluginsSystem.uiPlugins (plugins_system.ts:274:36) │ERROR at PluginsService.discover (plugins_service.ts:120:58) │ERROR at Server.preboot (server.ts:172:30) │ERROR at Root.preboot (index.ts:57:14) │ERROR at extract_field_lists_from_plugins_worker.ts:51:3 ERROR UNHANDLED ERROR ERROR Error: worker exited without sending mappings at extractFieldListsFromPlugins (extract_field_lists_from_plugins.ts:62:11) at processTicksAndRejections (node:internal/process/task_queues:95:5) at runModelVersionMappingAdditionsChecks (run_versions_mapping_additions_check.ts:28:66) at run_check_mappings_update_cli.ts:23:9 at tooling_log.ts:84:18 at description (run_check_mappings_update_cli.ts:22:7) at run.ts:73:10 at withProcRunner (with_proc_runner.ts:29:5) at run (run.ts:71:5) ``` </details> Also, the `storage` is passed by the `cases` plugin to the Kibana context and is not a dependency to another plugin. The script assumes that the `storage` is a plugin dependency and reports it. Neverthelss, this demonstrates that we should stop passing `storage` to the Kibana context and use hooks like `useLocalStorage`. ## Results from the script <img width="727" alt="Screenshot 2024-02-17 at 8 54 32 AM" src="https://github.com/elastic/kibana/assets/7871006/1c86f501-72c5-473c-92f5-7a4935da914b"> ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <[email protected]>
## Summary The script from elastic#171483 can identify inconsistencies and untyped dependencies in Kibana plugins. This PR fixes: - Move `esUiShared`, `kibanaReact`, `kibanaUtils`, and `savedObjectsFinder` to `requiredBundles` - Rename `CasesUiSetup` to `CasesPublicSetup` - Rename `CasesUiStart` to `CasesPublicStart` - Rename `CasesSetup` to `CasesServerSetup` - Rename `CasesStart` to `CasesServerStart` - Rename `CasesPluginSetup` (public) to `CasesPublicSetupDependencies` - Rename `CasesPluginStart` (public) to `CasesPublicStartDependencies` - Moved `PluginsSetup` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerSetupDependencies` - Moved `PluginsStart` from `server/plugin.ts` to `server/types.ts` and rename it to `CasesServerStartDependencies` I could not add the `apm` to optional plugins because I get an error due to circular dependencies. Observability and Secuirty solution have `apm` and `cases` as a dependency and probably this cause the following error. <details> <summary>Error</summary> ``` info Running model version mapping addition checks │ info Generating field lists from registry and file │ info Loading core with all plugins enabled so that we can get all savedObject mappings... │ERROR UNHANDLED ERROR: Error: Topological ordering of plugins did not complete, these plugins have cyclic or missing dependencies: ["aiAssistantManagementObservability","aiops","apm","assetManager","cases","cloudDefend","datasetQuality","elasticAssistant","enterpriseSearch","exploratoryView","infra","kubernetesSecurity","logsShared","logstash","metricsDataAccess","ml","monitoring","observability","observabilityAIAssistant","observabilityOnboarding","observabilityShared","observabilityLogsExplorer","osquery","profiling","securitySolution","securitySolutionEss","securitySolutionServerless","serverlessObservability","sessionView","synthetics","threatIntelligence","timelines","upgradeAssistant","uptime","ux"] │ERROR at PluginsSystem.getTopologicallySortedPluginNames (plugins_system.ts:354:13) │ERROR at PluginsSystem.uiPlugins (plugins_system.ts:274:36) │ERROR at PluginsService.discover (plugins_service.ts:120:58) │ERROR at Server.preboot (server.ts:172:30) │ERROR at Root.preboot (index.ts:57:14) │ERROR at extract_field_lists_from_plugins_worker.ts:51:3 ERROR UNHANDLED ERROR ERROR Error: worker exited without sending mappings at extractFieldListsFromPlugins (extract_field_lists_from_plugins.ts:62:11) at processTicksAndRejections (node:internal/process/task_queues:95:5) at runModelVersionMappingAdditionsChecks (run_versions_mapping_additions_check.ts:28:66) at run_check_mappings_update_cli.ts:23:9 at tooling_log.ts:84:18 at description (run_check_mappings_update_cli.ts:22:7) at run.ts:73:10 at withProcRunner (with_proc_runner.ts:29:5) at run (run.ts:71:5) ``` </details> Also, the `storage` is passed by the `cases` plugin to the Kibana context and is not a dependency to another plugin. The script assumes that the `storage` is a plugin dependency and reports it. Neverthelss, this demonstrates that we should stop passing `storage` to the Kibana context and use hooks like `useLocalStorage`. ## Results from the script <img width="727" alt="Screenshot 2024-02-17 at 8 54 32 AM" src="https://github.com/elastic/kibana/assets/7871006/1c86f501-72c5-473c-92f5-7a4935da914b"> ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <[email protected]>
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 lifecycleoptional
- the dependency can be missing or disabled -- will beundefined
in the lifecyclerequiredBundle
- the dependency is required as static code only -- will not be present in the lifecycleMissing 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:
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 as middleware for a dependent plugin. Unfortunately, while this dependency can be stated asrequired
in a dependent plugin'skibana.jsonc
, the fact that this is a side-effect makes it incredible difficult to understand the dependency without searching the code.So the side-effect is more or less hidden from developers. This is likely why we see other plugins using the lifecycle logic, or copy-pasting licensing check code [1, 2], 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.For side-effects, identifying them is key, and then refactoring the plugins to provide appropriate logic in the
start
orsetup
contracts.Next steps
--fix
optionI'm also considering (in another PR), outputting a consistent type definition file-- perhaps
kibana.d.ts
-- to the plugin from which the implementing classes couldOmit<>
orPick<>
the relevant contracts.