Merge pull request #18699 from SDA-SE/feat/default-mkdocs-plugins

feat: support custom default plugins for techdocs
This commit is contained in:
Morgan Bentell
2023-08-23 10:58:43 +02:00
committed by GitHub
12 changed files with 100 additions and 14 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-techdocs-backend': minor
'@backstage/plugin-techdocs-node': minor
'@techdocs/cli': minor
---
Add optional config and cli option for techdocs to specify default mkdocs plugins.
+1
View File
@@ -141,6 +141,7 @@ Options:
if not found.
--etag <ETAG> A unique identifier for the prepared tree e.g. commit SHA. If provided it will be stored
in techdocs_metadata.json.
--defaultPlugin <PLUGIN_NAME> Plugins which should be added automatically to the mkdocs.yaml file. (default: [])
--omitTechdocsCoreMkdocsPlugin An option to disable automatic addition of techdocs-core plugin to the mkdocs.yaml files.
Defaults to false, which means that the techdocs-core plugin is always added to the mkdocs file.
--legacyCopyReadmeMdToIndexMd Attempt to ensure an index.md exists falling back to using <docs-dir>/README.md or README.md
+9
View File
@@ -49,6 +49,15 @@ techdocs:
# will be broken in these scenarios.
legacyCopyReadmeMdToIndexMd: false
# (Optional) Configures the default plugins which should be added
# automatically to every mkdocs.yaml file. This simplifies the usage as
# e.g. styling plugins can be added once for all.
# Make sure that the defined plugins are installed locally / in the Docker
# image.
# By default, only the techdocs-core plugin will be added (except if
# omitTechdocsCorePlugin: true).
defaultPlugins: ['techdocs-core']
# techdocs.builder can be either 'local' or 'external'.
# Using the default build strategy, if builder is set to 'local' and you open a TechDocs page,
# techdocs-backend will try to generate the docs, publish to storage and show the generated docs afterwards.
+1
View File
@@ -37,6 +37,7 @@ Options:
-v --verbose
--omitTechdocsCoreMkdocsPlugin
--legacyCopyReadmeMdToIndexMd
--defaultPlugin [defaultPlugins...]
-h, --help
```
@@ -47,6 +47,7 @@ export default async function generate(opts: OptionValues) {
const dockerImage = opts.dockerImage;
const pullImage = opts.pull;
const legacyCopyReadmeMdToIndexMd = opts.legacyCopyReadmeMdToIndexMd;
const defaultPlugins = opts.defaultPlugin;
logger.info(`Using source dir ${sourceDir}`);
logger.info(`Will output generated files in ${outputDir}`);
@@ -68,6 +69,7 @@ export default async function generate(opts: OptionValues) {
mkdocs: {
legacyCopyReadmeMdToIndexMd,
omitTechdocsCorePlugin,
defaultPlugins,
},
},
},
@@ -70,6 +70,11 @@ export function registerCommands(program: Command) {
'Attempt to ensure an index.md exists falling back to using <docs-dir>/README.md or README.md in case a default <docs-dir>/index.md is not provided.',
false,
)
.option(
'--defaultPlugin [defaultPlugins...]',
'Plugins which should be added automatically to the mkdocs.yaml file',
[],
)
.alias('build')
.action(lazy(() => import('./generate/generate').then(m => m.default)));
+5
View File
@@ -57,6 +57,11 @@ export interface Config {
* will be broken in these scenarios.
*/
legacyCopyReadmeMdToIndexMd?: boolean;
/**
* List of mkdocs plugins which should be added as default to all mkdocs.yml files.
*/
defaultPlugins?: string[];
};
};
@@ -33,7 +33,7 @@ import {
} from './helpers';
import {
patchMkdocsYmlPreBuild,
pathMkdocsYmlWithTechdocsPlugin,
patchMkdocsYmlWithPlugins,
} from './mkdocsPatchers';
import yaml from 'js-yaml';
@@ -310,7 +310,7 @@ describe('helpers', () => {
});
});
describe('pathMkdocsYmlWithTechdocsPlugin', () => {
describe('patchMkdocsYmlWithPlugins', () => {
beforeEach(() => {
mockFs({
'/mkdocs_with_techdocs_plugin.yml': mkdocsYmlWithTechdocsPlugins,
@@ -319,7 +319,7 @@ describe('helpers', () => {
});
});
it('should not add additional plugins if techdocs exists already in mkdocs file', async () => {
await pathMkdocsYmlWithTechdocsPlugin(
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_techdocs_plugin.yml',
mockLogger,
);
@@ -334,7 +334,7 @@ describe('helpers', () => {
expect(parsedYml.plugins).toContain('techdocs-core');
});
it("should add the needed plugin if it doesn't exist in mkdocs file", async () => {
await pathMkdocsYmlWithTechdocsPlugin(
await patchMkdocsYmlWithPlugins(
'/mkdocs_without_plugins.yml',
mockLogger,
);
@@ -347,7 +347,7 @@ describe('helpers', () => {
expect(parsedYml.plugins).toContain('techdocs-core');
});
it('should not override existing plugins', async () => {
await pathMkdocsYmlWithTechdocsPlugin(
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_additional_plugins.yml',
mockLogger,
);
@@ -362,6 +362,23 @@ describe('helpers', () => {
expect(parsedYml.plugins).toContain('not-techdocs-core');
expect(parsedYml.plugins).toContain('also-not-techdocs-core');
});
it('should add all provided default plugins', async () => {
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_additional_plugins.yml',
mockLogger,
['techdocs-core', 'custom-plugin'],
);
const updatedMkdocsYml = await fs.readFile(
'/mkdocs_with_additional_plugins.yml',
);
const parsedYml = yaml.load(updatedMkdocsYml.toString()) as {
plugins: string[];
};
expect(parsedYml.plugins).toHaveLength(4);
expect(parsedYml.plugins).toContain('techdocs-core');
expect(parsedYml.plugins).toContain('custom-plugin');
});
});
describe('patchIndexPreBuild', () => {
@@ -138,27 +138,34 @@ export const patchMkdocsYmlPreBuild = async (
* Update the mkdocs.yml file before TechDocs generator uses it to generate docs site.
*
* List of tasks:
* - Add techdocs-core plugin to mkdocs file if it doesn't exist
* - Add all provided default plugins
*
* This function will not throw an error since this is not critical to the whole TechDocs pipeline.
* Instead it will log warnings if there are any errors in reading, parsing or writing YAML.
*
* @param mkdocsYmlPath - Absolute path to mkdocs.yml or equivalent of a docs site
* @param logger - A logger instance
* @param defaultPlugins - List of default mkdocs plugins
*/
export const pathMkdocsYmlWithTechdocsPlugin = async (
export const patchMkdocsYmlWithPlugins = async (
mkdocsYmlPath: string,
logger: Logger,
defaultPlugins: string[] = ['techdocs-core'],
) => {
await patchMkdocsFile(mkdocsYmlPath, logger, mkdocsYml => {
// Modify mkdocs.yaml to contain the needed techdocs-core plugin if it is not there
// Modify mkdocs.yaml to contain the required default plugins
if (!('plugins' in mkdocsYml)) {
mkdocsYml.plugins = ['techdocs-core'];
mkdocsYml.plugins = defaultPlugins;
return true;
}
if (mkdocsYml.plugins && !mkdocsYml.plugins.includes('techdocs-core')) {
mkdocsYml.plugins.push('techdocs-core');
if (
mkdocsYml.plugins &&
!defaultPlugins.every(plugin => mkdocsYml.plugins!.includes(plugin))
) {
mkdocsYml.plugins = [
...new Set([...mkdocsYml.plugins, ...defaultPlugins]),
];
return true;
}
return false;
@@ -157,4 +157,24 @@ describe('readGeneratorConfig', () => {
legacyCopyReadmeMdToIndexMd: true,
});
});
it('should read the default plugins config', () => {
const config = new ConfigReader({
techdocs: {
generator: {
runIn: 'docker',
dockerImage: 'my-org/techdocs',
pullImage: false,
mkdocs: { defaultPlugins: ['mkdocs-custom-plugin'] },
},
},
});
expect(readGeneratorConfig(config, logger)).toEqual({
runIn: 'docker',
dockerImage: 'my-org/techdocs',
pullImage: false,
defaultPlugins: ['mkdocs-custom-plugin'],
});
});
});
@@ -33,7 +33,7 @@ import {
import {
patchMkdocsYmlPreBuild,
pathMkdocsYmlWithTechdocsPlugin,
patchMkdocsYmlWithPlugins,
} from './mkdocsPatchers';
import {
GeneratorBase,
@@ -121,10 +121,18 @@ export class TechdocsGenerator implements GeneratorBase {
await patchIndexPreBuild({ inputDir, logger: childLogger, docsDir });
}
if (!this.options.omitTechdocsCoreMkdocsPlugin) {
await pathMkdocsYmlWithTechdocsPlugin(mkdocsYmlPath, childLogger);
// patch the list of mkdocs plugins
const defaultPlugins = this.options.defaultPlugins ?? [];
if (
!this.options.omitTechdocsCoreMkdocsPlugin &&
!defaultPlugins.includes('techdocs-core')
) {
defaultPlugins.push('techdocs-core');
}
await patchMkdocsYmlWithPlugins(mkdocsYmlPath, childLogger, defaultPlugins);
// Directories to bind on container
const mountDirs = {
[inputDir]: '/input',
@@ -233,5 +241,8 @@ export function readGeneratorConfig(
legacyCopyReadmeMdToIndexMd: config.getOptionalBoolean(
'techdocs.generator.mkdocs.legacyCopyReadmeMdToIndexMd',
),
defaultPlugins: config.getOptionalStringArray(
'techdocs.generator.mkdocs.defaultPlugins',
),
};
}
@@ -41,6 +41,7 @@ export type GeneratorConfig = {
pullImage?: boolean;
omitTechdocsCoreMkdocsPlugin?: boolean;
legacyCopyReadmeMdToIndexMd?: boolean;
defaultPlugins?: string[];
};
/**