mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
## Summary Addresses https://github.com/elastic/kibana-team/issues/1175 As part of the **Sustainable Kibana Architecture** initiative, this PR sets the foundation to start classifying plugins in isolated groups, matching our current solutions / project types: * It adds support for the following fields in the packages' manifests (kibana.jsonc): * `group?: 'search' | 'security' | 'observability' | 'platform' | 'common'` * `visibility?: 'private' | 'shared'` * It proposes a folder structure to automatically infer groups: ```javascript 'src/platform/plugins/shared': { group: 'platform', visibility: 'shared', }, 'src/platform/plugins/internal': { group: 'platform', visibility: 'private', }, 'x-pack/platform/plugins/shared': { group: 'platform', visibility: 'shared', }, 'x-pack/platform/plugins/internal': { group: 'platform', visibility: 'private', }, 'x-pack/solutions/observability/plugins': { group: 'observability', visibility: 'private', }, 'x-pack/solutions/security/plugins': { group: 'security', visibility: 'private', }, 'x-pack/solutions/search/plugins': { group: 'search', visibility: 'private', }, ``` * If a plugin is moved to one of the specific locations above, the group and visibility in the manifest (if specified) must match those inferred from the path. * Plugins that are not relocated are considered: `group: 'common', visibility: 'shared'` by default. As soon as we specify a custom `group`, the ESLINT rules will check violations against dependencies / dependants. The ESLINT rules are pretty simple: * Plugins can only depend on: * Plugins in the same group * OR plugins with `'shared'` visibility * Plugins in `'observability', 'security', 'search'` groups are mandatorily `'private'`. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
/*
|
|
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
*/
|
|
|
|
import { join } from 'path';
|
|
import { writeFile } from 'fs/promises';
|
|
import { flatMap, unset } from 'lodash';
|
|
import { set } from '@kbn/safer-lodash-set';
|
|
import type { ToolingLog } from '@kbn/tooling-log';
|
|
import type { Flags } from '@kbn/dev-cli-runner';
|
|
import { type Package, getPackages } from '@kbn/repo-packages';
|
|
import { REPO_ROOT } from '@kbn/repo-info';
|
|
|
|
const MANIFEST_FILE = 'kibana.jsonc';
|
|
|
|
const getKibanaJsonc = (flags: Flags, log: ToolingLog): Package[] => {
|
|
const modules = getPackages(REPO_ROOT);
|
|
|
|
let packageIds: string[] = [];
|
|
let pluginIds: string[] = [];
|
|
|
|
if (typeof flags.package === 'string') {
|
|
packageIds = [flags.package].filter(Boolean);
|
|
} else if (Array.isArray(flags.package)) {
|
|
packageIds = [...flags.package].filter(Boolean);
|
|
}
|
|
|
|
if (typeof flags.plugin === 'string') {
|
|
pluginIds = [flags.plugin].filter(Boolean);
|
|
} else if (Array.isArray(flags.plugin)) {
|
|
pluginIds = [...flags.plugin].filter(Boolean);
|
|
}
|
|
|
|
return modules.filter(
|
|
(pkg) =>
|
|
packageIds.includes(pkg.id) || (pkg.isPlugin() && pluginIds.includes(pkg.manifest.plugin.id))
|
|
);
|
|
};
|
|
|
|
export const listManifestFiles = (flags: Flags, log: ToolingLog) => {
|
|
const modules = getPackages(REPO_ROOT);
|
|
modules
|
|
.filter((module) => module.manifest.type === 'plugin')
|
|
.forEach((module) => {
|
|
log.info(join(module.directory, MANIFEST_FILE), module.id);
|
|
});
|
|
};
|
|
|
|
export const printManifest = (flags: Flags, log: ToolingLog) => {
|
|
const kibanaJsoncs = getKibanaJsonc(flags, log);
|
|
kibanaJsoncs.forEach((kibanaJsonc) => {
|
|
const manifestPath = join(kibanaJsonc.directory, MANIFEST_FILE);
|
|
log.info('\n\nShowing manifest: ', manifestPath);
|
|
log.info(JSON.stringify(kibanaJsonc, null, 2));
|
|
});
|
|
};
|
|
|
|
export const updateManifest = async (flags: Flags, log: ToolingLog) => {
|
|
let toSet: string[] = [];
|
|
let toUnset: string[] = [];
|
|
|
|
if (typeof flags.set === 'string') {
|
|
toSet = [flags.set].filter(Boolean);
|
|
} else if (Array.isArray(flags.set)) {
|
|
toSet = [...flags.set].filter(Boolean);
|
|
}
|
|
|
|
if (typeof flags.unset === 'string') {
|
|
toUnset = [flags.unset].filter(Boolean);
|
|
} else if (Array.isArray(flags.unset)) {
|
|
toUnset = [...flags.unset].filter(Boolean);
|
|
}
|
|
|
|
if (!toSet.length && !toUnset.length) {
|
|
// no need to update anything
|
|
return;
|
|
}
|
|
|
|
const kibanaJsoncs = getKibanaJsonc(flags, log);
|
|
|
|
for (let i = 0; i < kibanaJsoncs.length; ++i) {
|
|
const kibanaJsonc = kibanaJsoncs[i];
|
|
|
|
if (kibanaJsonc?.manifest) {
|
|
const manifestPath = join(kibanaJsonc.directory, MANIFEST_FILE);
|
|
log.info('Updating manifest: ', manifestPath);
|
|
toSet.forEach((propValue) => {
|
|
const [prop, value] = propValue.split('=');
|
|
log.info(`Setting "${prop}": "${value}"`);
|
|
set(kibanaJsonc.manifest, prop, value);
|
|
});
|
|
|
|
toUnset.forEach((prop) => {
|
|
log.info(`Removing "${prop}"`);
|
|
unset(kibanaJsonc.manifest, prop);
|
|
});
|
|
|
|
sanitiseManifest(kibanaJsonc);
|
|
|
|
await writeFile(manifestPath, JSON.stringify(kibanaJsonc.manifest, null, 2));
|
|
log.info('DONE');
|
|
}
|
|
}
|
|
};
|
|
|
|
const sanitiseManifest = (kibanaJsonc: Package) => {
|
|
kibanaJsonc.manifest.owner = flatMap(kibanaJsonc.manifest.owner.map((owner) => owner.split(' ')));
|
|
};
|