diff --git a/tools/docs/datasources.ts b/tools/docs/datasources.ts index 9d069ff49f79ce23c31ca40a3a2054b6f8fe41a4..aaec81fa262914ba9e343c1b81788486215e58ff 100644 --- a/tools/docs/datasources.ts +++ b/tools/docs/datasources.ts @@ -1,6 +1,7 @@ import { codeBlock } from 'common-tags'; import { getDatasources } from '../../lib/modules/datasource'; import { readFile, updateFile } from '../utils'; +import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items'; import { formatDescription, formatUrls, @@ -9,7 +10,10 @@ import { replaceContent, } from './utils'; -export async function generateDatasources(dist: string): Promise<void> { +export async function generateDatasources( + dist: string, + datasourceIssuesMap: OpenItems +): Promise<void> { const dsList = getDatasources(); let datasourceContent = '\nSupported values for `datasource` are:\n\n'; @@ -56,6 +60,8 @@ export async function generateDatasources(dist: string): Promise<void> { '\n```\n'; } + md += generateFeatureAndBugMarkdown(datasourceIssuesMap, datasource); + await updateFile(`${dist}/modules/datasource/${datasource}/index.md`, md); } diff --git a/tools/docs/github-query-items.ts b/tools/docs/github-query-items.ts index f9668ce59bbe18a63eb09fb4454a07a74dd276af..c03461c5f979a92a47d7b1430bcb9d2cc38601cb 100644 --- a/tools/docs/github-query-items.ts +++ b/tools/docs/github-query-items.ts @@ -1,4 +1,21 @@ -export type GithubApiQueryResponse = { +import { DateTime } from 'luxon'; +import { logger } from '../../lib/logger'; +import * as hostRules from '../../lib/util/host-rules'; +import { GithubHttp } from '../../lib/util/http/github'; +import { getQueryString } from '../../lib/util/url'; + +const gitHubApiUrl = 'https://api.github.com/search/issues?'; +const githubApi = new GithubHttp(); + +if (process.env.GITHUB_TOKEN) { + logger.debug('Using GITHUB_TOKEN from env'); + hostRules.add({ + matchHost: 'api.github.com', + token: process.env.GITHUB_TOKEN, + }); +} + +type GithubApiQueryResponse = { total_count: number; incomplete_results: boolean; items: ItemsEntity[]; @@ -14,3 +31,124 @@ export type ItemsEntity = { export type LabelsEntity = { name: string; }; + +export interface RenovateOpenItems { + managers: OpenItems; + platforms: OpenItems; + datasources: OpenItems; +} + +export type OpenItems = Record<string, Items | undefined>; + +export interface Items { + bugs: ItemsEntity[]; + features: ItemsEntity[]; +} + +export async function getOpenGitHubItems(): Promise<RenovateOpenItems> { + const q = `repo:renovatebot/renovate type:issue is:open -label:priority-5-triage`; + const per_page = 100; + try { + const query = getQueryString({ q, per_page }); + const res = await githubApi.getJson<GithubApiQueryResponse>( + gitHubApiUrl + query, + { + paginationField: 'items', + paginate: true, + } + ); + const rawItems = res.body?.items ?? []; + + const renovateOpenItems: RenovateOpenItems = { + managers: extractIssues(rawItems, 'manager:'), + platforms: extractIssues(rawItems, 'platform:'), + datasources: extractIssues(rawItems, 'datasource:'), + }; + + return renovateOpenItems; + } catch (err) { + logger.error({ err }, 'Error getting query results'); + throw err; + } +} + +function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems { + const issuesMap: OpenItems = {}; + + for (const item of items) { + const type = item.labels + .find((l) => l.name.startsWith('type:')) + ?.name.split(':')[1]; + if (!type) { + continue; + } + const label = item.labels + .find((l) => l.name.startsWith(labelPrefix)) + ?.name.split(':')[1]; + if (!label) { + continue; + } + if (!issuesMap[label]) { + issuesMap[label] = { bugs: [], features: [] }; + } + switch (type) { + case 'bug': + issuesMap[label]?.bugs.push(item); + break; + case 'feature': + issuesMap[label]?.features.push(item); + break; + default: + break; + } + } + + return issuesMap; +} + +function stringifyIssues(items: ItemsEntity[] | undefined): string { + if (!items) { + return ''; + } + let list = ''; + for (const item of items) { + list += ` - ${item.title} [#${item.number}](${item.html_url})\n`; + } + return list; +} + +export function generateFeatureAndBugMarkdown( + issuesMap: OpenItems, + key: string +): string { + let md = '\n\n'; + + const featureList = stringifyIssues(issuesMap[key]?.features); + const bugList = stringifyIssues(issuesMap[key]?.bugs); + + if (featureList || bugList) { + md += '## Open items\n\n'; + } + + if (featureList || bugList) { + const now = DateTime.utc().toFormat('MMMM dd, yyyy'); + const lists = `list of ${featureList ? 'features' : ''}${ + featureList && bugList ? ' and ' : '' + }${bugList ? 'bugs' : ''}`; + md += `The below ${lists} were current when this page was generated on ${now}.\n\n`; + } + + if (featureList) { + md += '### Feature requests\n\n'; + md += featureList; + md += '\n'; + } + + if (bugList) { + md += '### Bug reports\n\n'; + md += bugList; + md += '\n'; + } + + return md; +} diff --git a/tools/docs/manager.ts b/tools/docs/manager.ts index a111f87e3bca18129c254dfcfb0cc5a94f75ea17..dddd4511f925ffa7f0a643819147a6cd237d88e4 100644 --- a/tools/docs/manager.ts +++ b/tools/docs/manager.ts @@ -1,29 +1,9 @@ -import { DateTime } from 'luxon'; import type { RenovateConfig } from '../../lib/config/types'; -import { logger } from '../../lib/logger'; import { getManagers } from '../../lib/modules/manager'; -import * as hostRules from '../../lib/util/host-rules'; -import { GithubHttp } from '../../lib/util/http/github'; -import { getQueryString } from '../../lib/util/url'; import { readFile, updateFile } from '../utils'; -import type { GithubApiQueryResponse, ItemsEntity } from './github-query-items'; +import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items'; import { getDisplayName, getNameWithUrl, replaceContent } from './utils'; -const gitHubApiUrl = 'https://api.github.com/search/issues?'; - -if (process.env.GITHUB_TOKEN) { - logger.debug('Using GITHUB_TOKEN from env'); - hostRules.add({ - matchHost: 'api.github.com', - token: process.env.GITHUB_TOKEN, - }); -} - -interface ManagerIssues { - bugs: ItemsEntity[]; - features: ItemsEntity[]; -} - function getTitle(manager: string, displayName: string): string { if (manager === 'regex') { return `Custom Manager Support using Regex`; @@ -35,84 +15,12 @@ function getManagerLink(manager: string): string { return `[\`${manager}\`](${manager}/)`; } -function stringifyIssues(items: ItemsEntity[]): [string, number] { - if (!items) { - return ['', 0]; - } - let list = ''; - for (const item of items) { - list += ` - ${item.title} [#${item.number}](${item.html_url})\n`; - } - return [list, items.length]; -} - -function extractIssues( - managerIssuesMap: Record<string, ManagerIssues>, - items: ItemsEntity[] -): void { - if (!items || !managerIssuesMap) { - return; - } - for (const item of items) { - const type = item.labels - .find((l) => l.name.startsWith('type:')) - ?.name.split(':')[1]; - if (!type) { - continue; - } - const manager = item.labels - .find((l) => l.name.startsWith('manager:')) - ?.name.split(':')[1]; - if (!manager) { - continue; - } - if (!managerIssuesMap[manager]) { - managerIssuesMap[manager] = { bugs: [], features: [] }; - } - switch (type) { - case 'bug': - managerIssuesMap[manager].bugs.push(item); - break; - case 'feature': - managerIssuesMap[manager].features.push(item); - break; - default: - break; - } - } -} - -export async function getManagersGitHubIssues(): Promise< - Record<string, ManagerIssues> -> { - const q = `repo:renovatebot/renovate type:issue is:open -label:priority-5-triage`; - const per_page = 100; - const managerIssuesMap: Record<string, ManagerIssues> = {}; - const githubApi = new GithubHttp('manager-issues'); - try { - const query = getQueryString({ q, per_page }); - const res = await githubApi.getJson<GithubApiQueryResponse>( - gitHubApiUrl + query, - { - paginationField: 'items', - paginate: true, - } - ); - const items = res.body?.items ?? []; - extractIssues( - managerIssuesMap, - items.sort((a, b) => a.number - b.number) - ); - } catch (err) { - logger.error({ err }, 'Error getting query results'); - throw err; - } - return managerIssuesMap; -} - -export async function generateManagers(dist: string): Promise<void> { +export async function generateManagers( + dist: string, + managerIssuesMap: OpenItems +): Promise<void> { const managers = getManagers(); - const managerIssuesMap = await getManagersGitHubIssues(); + const allLanguages: Record<string, string[]> = {}; for (const [manager, definition] of managers) { const language = definition.language ?? 'other'; @@ -173,30 +81,9 @@ sidebar_label: ${displayName} if (manager !== 'regex') { md += '\n## Additional Information\n\n'; } - md += managerReadmeContent + '\n\n'; - - const [featureList] = stringifyIssues(managerIssuesMap[manager]?.features); - if (featureList) { - md += '## Open feature requests\n\n'; - md += featureList; - md += '\n'; - } + md += managerReadmeContent; - const [bugList] = stringifyIssues(managerIssuesMap[manager]?.bugs); - if (bugList) { - md += '## Open bug reports\n\n'; - md += bugList; - md += '\n'; - } - - if (featureList || bugList) { - const now = DateTime.utc().toFormat('MMMM dd, yyyy'); - const lists = `list of ${featureList ? 'features' : ''}${ - featureList && bugList ? ' and ' : '' - }${bugList ? 'bugs' : ''}`; - md += '\n\n'; - md += `The above ${lists} were current when this page was generated on ${now}.\n`; - } + md += generateFeatureAndBugMarkdown(managerIssuesMap, manager); await updateFile(`${dist}/modules/manager/${manager}/index.md`, md); } diff --git a/tools/docs/platforms.ts b/tools/docs/platforms.ts index e6ac36e1d4d62f2a700ec162620498a4f551bf5d..aa83fa273b6bbe1bd27232d0bc0498c1a681a956 100644 --- a/tools/docs/platforms.ts +++ b/tools/docs/platforms.ts @@ -1,13 +1,20 @@ import { getPlatformList } from '../../lib/modules/platform'; import { readFile, updateFile } from '../utils'; +import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items'; import { getModuleLink, replaceContent } from './utils'; -export async function generatePlatforms(dist: string): Promise<void> { +export async function generatePlatforms( + dist: string, + platformIssuesMap: OpenItems +): Promise<void> { let platformContent = 'Supported values for `platform` are: '; const platforms = getPlatformList(); for (const platform of platforms) { - const readme = await readFile(`lib/modules/platform/${platform}/index.md`); - await updateFile(`${dist}/modules/platform/${platform}/index.md`, readme); + let md = await readFile(`lib/modules/platform/${platform}/index.md`); + + md += generateFeatureAndBugMarkdown(platformIssuesMap, platform); + + await updateFile(`${dist}/modules/platform/${platform}/index.md`, md); } platformContent += platforms @@ -16,8 +23,7 @@ export async function generatePlatforms(dist: string): Promise<void> { platformContent += '.\n'; - const indexFileName = `docs/usage/modules/platform/index.md`; - let indexContent = await readFile(indexFileName); + let indexContent = await readFile(`docs/usage/modules/platform/index.md`); indexContent = replaceContent(indexContent, platformContent); await updateFile(`${dist}/modules/platform/index.md`, indexContent); } diff --git a/tools/generate-docs.ts b/tools/generate-docs.ts index d4434bf67d293319a3c0a638abf880ba0410728d..6b3f707ba092c8bdb6b33c6c9a4f1ebc89aad68e 100644 --- a/tools/generate-docs.ts +++ b/tools/generate-docs.ts @@ -3,6 +3,7 @@ import shell from 'shelljs'; import { getProblems, logger } from '../lib/logger'; import { generateConfig } from './docs/config'; import { generateDatasources } from './docs/datasources'; +import { getOpenGitHubItems } from './docs/github-query-items'; import { generateManagers } from './docs/manager'; import { generateManagerAsdfSupportedPlugins } from './docs/manager-asdf-supported-plugins'; import { generatePlatforms } from './docs/platforms'; @@ -36,8 +37,11 @@ process.on('unhandledRejection', (err) => { return; } + logger.info('* fetching open github issues'); + const openItems = await getOpenGitHubItems(); + logger.info('* platforms'); - await generatePlatforms(dist); + await generatePlatforms(dist, openItems.platforms); // versionings logger.info('* versionings'); @@ -45,11 +49,11 @@ process.on('unhandledRejection', (err) => { // datasources logger.info('* datasources'); - await generateDatasources(dist); + await generateDatasources(dist, openItems.datasources); // managers logger.info('* managers'); - await generateManagers(dist); + await generateManagers(dist, openItems.managers); // managers/asdf supported plugins logger.info('* managers/asdf/supported-plugins');