diff --git a/docs/development/local-development.md b/docs/development/local-development.md index 5bdfdef3dcc18f7873bcf42e180b4955fdf4e578..6a20f6988f3e1285b99ac167adcd8416c89d46b4 100644 --- a/docs/development/local-development.md +++ b/docs/development/local-development.md @@ -182,6 +182,9 @@ You can run `pnpm build:docs` to generate the docs. Then use `pnpm mkdocs serve` to preview the documentation locally. The docs will update automatically when you run `pnpm build:docs` again, no need to stop the `pnpm mkdocs serve` command. +Also, make sure to set the `GITHUB_TOKEN` in your environment as we use [gh cli](https://cli.github.com/manual/gh_issue_list) to fetch the issues. +If you wish to skip fetching the issues, then set `SKIP_GITHUB_ISSUES` to `true` before building the docs. + ## Keeping your Renovate fork up to date First of all, never commit to the `main` branch of your fork - always use a "feature" branch like `feat/1234-add-yarn-parsing`. diff --git a/tools/docs/github-query-items.ts b/tools/docs/github-query-items.ts index 1683158541a861764294dc3097357e34f30a39f5..e98be701b7b50da8d44cf4cfd4864dc8d4e8a0db 100644 --- a/tools/docs/github-query-items.ts +++ b/tools/docs/github-query-items.ts @@ -1,31 +1,14 @@ import { DateTime } from 'luxon'; +import { z } from 'zod'; 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.info('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[]; -}; +import { exec } from '../../lib/util/exec'; export type ItemsEntity = { - html_url: string; + url: string; number: number; title: string; labels: LabelsEntity[]; + issueType: 'Bug' | 'Feature'; }; export type LabelsEntity = { @@ -46,6 +29,34 @@ export interface Items { features: ItemsEntity[]; } +const GhOutputSchema = z.array( + z.object({ + url: z.string(), + title: z.string(), + labels: z.array( + z.object({ + name: z.string(), + }), + ), + number: z.number(), + }), +); + +async function getIssuesByIssueType( + issueType: 'Bug' | 'Feature', +): Promise<ItemsEntity[]> { + const command = `gh issue list --json "title,number,url,labels" --search "type:${issueType}" --limit 1000`; + const execRes = await exec(command); + const res = GhOutputSchema.safeParse(JSON.parse(execRes.stdout)); + if (res.error) { + throw res.error; + } + + return res.data.map((issue) => { + return { ...issue, issueType }; + }); +} + export async function getOpenGitHubItems(): Promise<RenovateOpenItems> { const result: RenovateOpenItems = { managers: {}, @@ -59,18 +70,21 @@ export async function getOpenGitHubItems(): Promise<RenovateOpenItems> { return result; } - const q = `repo:renovatebot/renovate type:issue is:open`; - const per_page = 100; + if (!process.env.GITHUB_TOKEN) { + logger.warn( + 'No GITHUB_TOKEN found in env, cannot fetch Github issues. Skipping...', + ); + return result; + } + + if (process.env.CI) { + return result; + } + try { - const query = getQueryString({ q, per_page }); - const res = await githubApi.getJsonUnchecked<GithubApiQueryResponse>( - gitHubApiUrl + query, - { - paginationField: 'items', - paginate: true, - }, + const rawItems = (await getIssuesByIssueType('Bug')).concat( + await getIssuesByIssueType('Feature'), ); - const rawItems = res.body?.items ?? []; result.managers = extractIssues(rawItems, 'manager:'); result.platforms = extractIssues(rawItems, 'platform:'); @@ -91,9 +105,8 @@ 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]; + const type = item.issueType; + if (!type) { continue; } @@ -107,10 +120,10 @@ function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems { issuesMap[label] = { bugs: [], features: [] }; } switch (type) { - case 'bug': + case 'Bug': issuesMap[label]?.bugs.push(item); break; - case 'feature': + case 'Feature': issuesMap[label]?.features.push(item); break; default: @@ -127,7 +140,7 @@ function stringifyIssues(items: ItemsEntity[] | undefined): string { } let list = ''; for (const item of items) { - list += ` - ${item.title} [#${item.number}](${item.html_url})\n`; + list += ` - ${item.title} [#${item.number}](${item.url})\n`; } return list; }