import { codeBlock } from 'common-tags'; import type { RenovateConfig } from '../../lib/config/types.ts'; import type { Category } from '../../lib/constants/index.ts'; import { getCustomManagers, isCustomManager, } from '../../lib/modules/manager/custom/index.ts'; import { getManagers } from '../../lib/modules/manager/index.ts'; import { readFile, updateFile } from '../utils/index.ts'; import type { OpenItems } from './github-query-items.ts'; import { generateFeatureAndBugMarkdown } from './github-query-items.ts'; import { formatUrls, getDisplayName, getModuleLink, getNameWithUrl, replaceContent, } from './utils.ts'; const noCategoryID = 'no-category'; const noCategoryDisplayName = 'No Category'; function getTitle( manager: string, displayName: string, isCustomMgr: boolean, ): string { if (isCustomMgr) { return `Custom Manager Support using ${displayName}`; } return `Automated Dependency Updates for ${displayName}`; } function getManagerLink(manager: string): string { return getModuleLink(manager, `\`${manager}\``); } export const CategoryNames: Record = { ansible: 'Ansible', batect: 'Batect', bazel: 'Bazel', c: 'C and C++', cd: 'Continuous Delivery', ci: 'Continuous Integration', custom: 'Custom Managers', dart: 'Dart', docker: 'Docker', dotnet: '.NET', elixir: 'Elixir', golang: 'Go', haskell: 'Haskell', helm: 'Helm', iac: 'Infrastructure as Code', java: 'Java', js: 'JavaScript', kubernetes: 'Kubernetes', node: 'Node.js', perl: 'Perl', php: 'PHP', python: 'Python', ruby: 'Ruby', rust: 'Rust', swift: 'Swift', terraform: 'Terraform', }; export async function generateManagers( dist: string, managerIssuesMap: OpenItems, ): Promise { const allManagers = [...getManagers(), ...getCustomManagers()]; const allCategories: Record = {}; for (const [manager, definition] of allManagers) { const { defaultConfig, supportedDatasources, urls } = definition; const { managerFilePatterns } = defaultConfig as RenovateConfig; const displayName = getDisplayName(manager, definition); const isCustomMgr = isCustomManager(manager); const categories = definition.categories ?? [noCategoryID]; for (const category of categories) { allCategories[category] ??= []; allCategories[category].push(manager); } let md = codeBlock` --- title: ${getTitle(manager, displayName, isCustomMgr)} sidebar_label: ${displayName} edit_url: https://github.com/renovatebot/renovate/edit/main/lib/modules/manager/${ isCustomMgr ? `custom/${manager}` : manager }/readme.md --- `; md += '\n\n'; md += '**Categories**: '; if (categories.length) { for (let i = 0; i < categories.length; i++) { const category = categories[i]; if (i < categories.length - 1) { md += `\`${category}\`, `; } else { md += `\`${category}\``; } } } md += '\n\n'; if (!isCustomMgr) { const nameWithUrl = getNameWithUrl(manager, definition); md += `Renovate supports updating ${nameWithUrl} dependencies.\n\n`; if (defaultConfig.enabled === false) { md += '## Enabling\n\n'; md += `${displayName} functionality is currently in beta testing, so you must opt-in to test it. To enable it, add a configuration like this to either your self-hosted config or your \`renovate.json\`:\n\n`; md += '```\n'; md += `{\n "${manager}": {\n "enabled": true\n }\n}`; md += '\n```\n\n'; md += 'If you find any bugs, please [create a new discussion first](https://github.com/renovatebot/renovate/discussions/new). If you find that it works well, then let us know too.\n\n'; } md += '## File Matching\n\n'; if ( !Array.isArray(managerFilePatterns) || managerFilePatterns.length === 0 ) { md += `Because file names for \`${manager}\` cannot be easily determined automatically, Renovate will not attempt to match any \`${manager}\` files by default. `; } else { md += `By default, Renovate will check any files matching `; if (managerFilePatterns.length === 1) { md += `the following regular expression: \`${managerFilePatterns[0]}\`.\n\n`; } else { md += `any of the following regular expressions:\n\n`; md += '```\n'; md += managerFilePatterns.join('\n'); md += '\n```\n\n'; } } md += `For details on how to extend a manager's \`managerFilePatterns\` value, please follow [this link](../index.md#file-matching).\n\n`; md += '## Supported datasources\n\n'; const escapedDatasources = Array.from(new Set(supportedDatasources || [])) .sort() .map( (datasource) => `[\`${datasource}\`](../../datasource/${datasource}/index.md)`, ) .join(', '); md += `This manager supports extracting the following datasources: ${escapedDatasources}.\n\n`; md += formatUrls(urls); md += '## Dependency types\n\n'; if (definition.knownDepTypes?.length) { const hasPrettyDepType = definition.knownDepTypes.some( (m) => m.prettyDepType, ); md += 'This manager extracts the following `depType` values:\n\n'; if (hasPrettyDepType) { md += '| `depType` | `prettyDepType` | Description |\n'; md += '|-----------|-----------------|-------------|\n'; for (const { depType, prettyDepType, description, } of definition.knownDepTypes) { md += `| \`${depType}\` | ${prettyDepType ? `\`${prettyDepType}\`` : ''} | ${description} |\n`; } } else { md += '| `depType` | Description |\n'; md += '|-----------|-------------|\n'; for (const { depType, description } of definition.knownDepTypes) { md += `| \`${depType}\` | ${description} |\n`; } } md += '\n'; } if (definition.supportsDynamicDepTypesNote) { md += `${definition.supportsDynamicDepTypesNote}\n\n`; } if ( (!definition.knownDepTypes || definition.knownDepTypes.length === 0) && definition.supportsDynamicDepTypesNote === undefined ) { md += 'This manager has no documented `depType` values.\n'; } md += '## Default config\n\n'; md += '```json\n'; md += `${JSON.stringify(definition.defaultConfig, null, 2)}\n`; md += '```\n\n'; } const managerReadmeContent = await readFile( `lib/modules/manager/${ isCustomMgr ? `custom/${manager}` : manager }/readme.md`, ); if (!isCustomMgr && definition.supportsLockFileMaintenance) { md += '\n## Lock File Maintenance\n\n'; md += 'This manager supports [`lockFileMaintenance`](../../../configuration-options.md#lockfilemaintenance) for the following file(s):\n'; md += '\n'; for (const lockFile of definition.lockFileNames) { md += `- \`${lockFile}\`\n`; } md += '\n'; const delegated = definition.lockFileMaintenanceIsDelegatedToPackageManager; if (typeof delegated === 'string') { md += `${delegated}\n\n`; } else if (delegated === true) { md += 'Lock file maintenance is delegated to the underlying package manager, which Renovate runs as an external command.\n\n'; } else if (delegated === false) { md += 'Renovate performs lock file maintenance itself, without calling the underlying package manager.\n\n'; } } if (!isCustomMgr) { md += '\n## Additional Information\n\n'; } md += managerReadmeContent; md += generateFeatureAndBugMarkdown(managerIssuesMap, manager); await updateFile(`${dist}/modules/manager/${manager}/index.md`, md); } // add noCategoryDisplayName as last option const categories = Object.keys(allCategories).filter( (category) => category !== noCategoryID, ); categories.sort(); categories.push(noCategoryID); let categoryText = '\n'; categoryText += '| Group | Category ID | Managers |\n'; categoryText += '| :-- | :-- | :-- |\n'; for (const category of categories) { const managerLinkList = allCategories[category] .map(getManagerLink) .join(', '); const displayName = CategoryNames[category as Category] ?? noCategoryDisplayName; const massagedCategory = category === noCategoryID ? 'n/a' : `\`${category}\``; categoryText += `| ${displayName} | ${massagedCategory} | ${managerLinkList} | \n`; } let indexContent = await readFile(`docs/usage/modules/manager/index.md`); indexContent = replaceContent(indexContent, categoryText); await updateFile(`${dist}/modules/manager/index.md`, indexContent); }