From 26cf208e2cf3b66f6dacd31a7c8c0e35570a35eb Mon Sep 17 00:00:00 2001 From: Gabriel-Ladzaretti <97394622+Gabriel-Ladzaretti@users.noreply.github.com> Date: Sat, 25 Jun 2022 11:10:35 +0300 Subject: [PATCH] docs(core/config): Experimental feature handling (#16183) --- docs/usage/self-hosted-configuration.md | 4 -- lib/config/options/index.ts | 1 + lib/config/types.ts | 8 ++- test/website-docs.spec.ts | 1 - tools/docs/config.ts | 76 +++++++++++++++++++++++-- 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index 232e8239e2..998f6d9a2b 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -610,10 +610,6 @@ Set this to `"enabled"` to have Renovate maintain a JSON file cache per-reposito Set to `"reset"` if you ever need to bypass the cache and have it overwritten. JSON files will be stored inside the `cacheDir` beside the existing file-based package cache. -<!-- prettier-ignore --> -!!! warning - This is an experimental feature and may be modified or removed in a future non-major release. - ## requireConfig By default, Renovate needs a Renovate config file in each repository where it runs before it will propose any dependency updates. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index a5b945ad1c..aabe34a195 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -223,6 +223,7 @@ const options: RenovateOptions[] = [ allowedValues: ['disabled', 'enabled', 'reset'], stage: 'repository', default: 'disabled', + experimental: true, }, { name: 'force', diff --git a/lib/config/types.ts b/lib/config/types.ts index 9fde0ac330..186b3cb50d 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -341,9 +341,13 @@ export interface RenovateOptionBase { // used by tests relatedOptions?: string[]; - releaseStatus?: 'alpha' | 'beta' | 'unpublished'; - stage?: RenovateConfigStage; + + experimental?: boolean; + + experimentalDescription?: string; + + experimentalIssues?: number[]; } export interface RenovateArrayOption< diff --git a/test/website-docs.spec.ts b/test/website-docs.spec.ts index 3895146656..b398d2bb64 100644 --- a/test/website-docs.spec.ts +++ b/test/website-docs.spec.ts @@ -33,7 +33,6 @@ describe('website-docs', () => { .match(/\n## (.*?)\n/g) ?.map((match) => match.substring(4, match.length - 1)); const expectedOptions = options - .filter((option) => option.releaseStatus !== 'unpublished') .filter((option) => !option.globalOnly) .filter((option) => !option.parent) .filter((option) => !option.autogenerated) diff --git a/tools/docs/config.ts b/tools/docs/config.ts index 7a2a412424..110773c666 100644 --- a/tools/docs/config.ts +++ b/tools/docs/config.ts @@ -1,10 +1,12 @@ import stringify from 'json-stringify-pretty-compact'; import { getOptions } from '../../lib/config/options'; +import { getManagerList } from '../../lib/modules/manager'; import { getCliName } from '../../lib/workers/global/config/parse/cli'; import { getEnvName } from '../../lib/workers/global/config/parse/env'; import { readFile, updateFile } from '../utils'; const options = getOptions(); +const managers = new Set(getManagerList()); /** * Merge string arrays one by one @@ -86,6 +88,9 @@ function genTable(obj: [string, string][], type: string, def: any): string { 'allowString', 'admin', 'globalOnly', + 'experimental', + 'experimentalDescription', + 'experimentalIssues', ]; obj.forEach(([key, val]) => { const el = [key, val]; @@ -142,13 +147,59 @@ function genTable(obj: [string, string][], type: string, def: any): string { } function stringifyArrays(el: Record<string, any>): void { + const ignoredKeys = ['default', 'experimentalIssues']; + for (const [key, value] of Object.entries(el)) { - if (key !== 'default' && Array.isArray(value)) { + if (!ignoredKeys.includes(key) && Array.isArray(value)) { el[key] = value.join(', '); } } } +function genExperimentalMsg(el: Record<string, any>): string { + const ghIssuesUrl = 'https://github.com/renovatebot/renovate/issues/'; + let warning = + '\n<!-- prettier-ignore -->\n!!! warning "This feature is flagged as experimental"\n'; + + if (el.experimentalDescription) { + warning += indent`${2}${el.experimentalDescription}`; + } else { + warning += indent`${2}Experimental features might be changed or even removed at any time.`; + } + + const issues = el.experimentalIssues ?? []; + if (issues.length > 0) { + warning += `<br>To track this feature visit the following GitHub ${ + issues.length > 1 ? 'issues' : 'issue' + } `; + warning += + (issues + .map((issue: number) => `[#${issue}](${ghIssuesUrl}${issue})`) + .join(', ') as string) + '.'; + } + + return warning + '\n'; +} + +function indexMarkdown(lines: string[]): Record<string, [number, number]> { + const indexed: Record<string, [number, number]> = {}; + + let optionName = ''; + let start = 0; + for (const [i, line] of lines.entries()) { + if (line.startsWith('## ') || line.startsWith('### ')) { + if (optionName) { + indexed[optionName] = [start, i - 1]; + } + start = i; + optionName = line.split(' ')[1]; + } + } + indexed[optionName] = [start, lines.length - 1]; + + return indexed; +} + export async function generateConfig(dist: string, bot = false): Promise<void> { let configFile = `configuration-options.md`; if (bot) { @@ -159,15 +210,24 @@ export async function generateConfig(dist: string, bot = false): Promise<void> { '\n' ); + const indexed = indexMarkdown(configOptionsRaw); + options - .filter((option) => option.releaseStatus !== 'unpublished') + .filter( + (option) => !!option.globalOnly === bot && !managers.has(option.name) + ) .forEach((option) => { - // TODO: fix types (#9610) + // TODO: fix types (#7154,#9610) const el: Record<string, any> = { ...option }; - let headerIndex = configOptionsRaw.indexOf(`## ${option.name}`); - if (headerIndex === -1) { - headerIndex = configOptionsRaw.indexOf(`### ${option.name}`); + + if (!indexed[option.name]) { + throw new Error( + `Config option "${option.name}" is missing an entry in ${configFile}` + ); } + + const [headerIndex, footerIndex] = indexed[option.name]; + el.cli = getCliName(option); el.env = getEnvName(option); stringifyArrays(el); @@ -175,6 +235,10 @@ export async function generateConfig(dist: string, bot = false): Promise<void> { configOptionsRaw[headerIndex] += `\n${option.description}\n\n` + genTable(Object.entries(el), option.type, option.default); + + if (el.experimental) { + configOptionsRaw[footerIndex] += genExperimentalMsg(el); + } }); await updateFile(`${dist}/${configFile}`, configOptionsRaw.join('\n')); -- GitLab