mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-27 06:56:26 +00:00
Merge 74e9069051
into da5c5ed3f6
This commit is contained in:
commit
9a9be5e800
3 changed files with 148 additions and 23 deletions
|
@ -85,3 +85,35 @@ mutation EnablePullRequestAutoMerge(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const getIssuesByIssueTypeQuery = `
|
||||||
|
query(
|
||||||
|
$queryStr: String!
|
||||||
|
$count: Int,
|
||||||
|
$cursor: String
|
||||||
|
) {
|
||||||
|
search(
|
||||||
|
type: ISSUE
|
||||||
|
first: $count,
|
||||||
|
after: $cursor,
|
||||||
|
query: $queryStr
|
||||||
|
) {
|
||||||
|
nodes {
|
||||||
|
... on Issue {
|
||||||
|
number
|
||||||
|
title
|
||||||
|
url
|
||||||
|
labels (first:5) {
|
||||||
|
nodes {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -40,6 +40,9 @@ export interface GithubHttpOptions extends HttpOptions {
|
||||||
interface GithubGraphqlRepoData<T = unknown> {
|
interface GithubGraphqlRepoData<T = unknown> {
|
||||||
repository?: T;
|
repository?: T;
|
||||||
}
|
}
|
||||||
|
interface GithubGraphqlSearchData<T = unknown> {
|
||||||
|
search?: T;
|
||||||
|
}
|
||||||
|
|
||||||
export type GithubGraphqlResponse<T = unknown> =
|
export type GithubGraphqlResponse<T = unknown> =
|
||||||
| {
|
| {
|
||||||
|
@ -422,6 +425,78 @@ export class GithubHttp extends Http<GithubHttpOptions> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async querySearchField<T = Record<string, unknown>>(
|
||||||
|
query: string,
|
||||||
|
fieldName: string,
|
||||||
|
options: GraphqlOptions = {},
|
||||||
|
): Promise<T[]> {
|
||||||
|
const result: T[] = [];
|
||||||
|
|
||||||
|
const { paginate = true } = options;
|
||||||
|
|
||||||
|
let optimalCount: null | number = null;
|
||||||
|
let count = getGraphqlPageSize(
|
||||||
|
fieldName,
|
||||||
|
options.count ?? MAX_GRAPHQL_PAGE_SIZE,
|
||||||
|
);
|
||||||
|
let limit = options.limit ?? 1000;
|
||||||
|
let cursor: string | null = null;
|
||||||
|
|
||||||
|
let isIterating = true;
|
||||||
|
while (isIterating) {
|
||||||
|
const res = await this.requestGraphql<GithubGraphqlSearchData<T>>(query, {
|
||||||
|
...options,
|
||||||
|
count: Math.min(count, limit),
|
||||||
|
cursor,
|
||||||
|
paginate,
|
||||||
|
});
|
||||||
|
const searchData = res?.data;
|
||||||
|
if (
|
||||||
|
is.nonEmptyObject(searchData) &&
|
||||||
|
!is.nullOrUndefined(searchData[fieldName])
|
||||||
|
) {
|
||||||
|
optimalCount = count;
|
||||||
|
|
||||||
|
const {
|
||||||
|
nodes = [],
|
||||||
|
edges = [],
|
||||||
|
pageInfo,
|
||||||
|
} = searchData[fieldName] as GraphqlPaginatedContent<T>;
|
||||||
|
result.push(...nodes);
|
||||||
|
result.push(...edges);
|
||||||
|
|
||||||
|
limit = Math.max(0, limit - nodes.length - edges.length);
|
||||||
|
|
||||||
|
if (limit === 0) {
|
||||||
|
isIterating = false;
|
||||||
|
} else if (paginate && pageInfo) {
|
||||||
|
const { hasNextPage, endCursor } = pageInfo;
|
||||||
|
if (hasNextPage && endCursor) {
|
||||||
|
cursor = endCursor;
|
||||||
|
} else {
|
||||||
|
isIterating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = Math.floor(count / 2);
|
||||||
|
if (count === 0) {
|
||||||
|
logger.warn({ query, options, res }, 'Error fetching GraphQL nodes');
|
||||||
|
isIterating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paginate) {
|
||||||
|
isIterating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optimalCount && optimalCount < MAX_GRAPHQL_PAGE_SIZE) {
|
||||||
|
setGraphqlPageSize(fieldName, optimalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async queryRepoField<T = Record<string, unknown>>(
|
async queryRepoField<T = Record<string, unknown>>(
|
||||||
query: string,
|
query: string,
|
||||||
fieldName: string,
|
fieldName: string,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { logger } from '../../lib/logger';
|
import { logger } from '../../lib/logger';
|
||||||
|
import { getIssuesByIssueTypeQuery } from '../../lib/modules/platform/github/graphql';
|
||||||
import * as hostRules from '../../lib/util/host-rules';
|
import * as hostRules from '../../lib/util/host-rules';
|
||||||
import { GithubHttp } from '../../lib/util/http/github';
|
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();
|
const githubApi = new GithubHttp();
|
||||||
|
|
||||||
if (process.env.GITHUB_TOKEN) {
|
if (process.env.GITHUB_TOKEN) {
|
||||||
|
@ -15,17 +14,12 @@ if (process.env.GITHUB_TOKEN) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type GithubApiQueryResponse = {
|
|
||||||
total_count: number;
|
|
||||||
incomplete_results: boolean;
|
|
||||||
items: ItemsEntity[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ItemsEntity = {
|
export type ItemsEntity = {
|
||||||
html_url: string;
|
html_url: string;
|
||||||
number: number;
|
number: number;
|
||||||
title: string;
|
title: string;
|
||||||
labels: LabelsEntity[];
|
labels: LabelsEntity[];
|
||||||
|
issueType: 'Bug' | 'Feature';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LabelsEntity = {
|
export type LabelsEntity = {
|
||||||
|
@ -46,6 +40,39 @@ export interface Items {
|
||||||
features: ItemsEntity[];
|
features: ItemsEntity[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getIssuesByIssueType(
|
||||||
|
issueType: 'Bug' | 'Feature',
|
||||||
|
): Promise<ItemsEntity[]> {
|
||||||
|
const queryString = `type:${issueType}, repo:renovatebot/renovate, state:open`;
|
||||||
|
const res = (await githubApi.querySearchField<unknown>(
|
||||||
|
getIssuesByIssueTypeQuery,
|
||||||
|
'search',
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
queryStr: queryString,
|
||||||
|
},
|
||||||
|
paginate: true,
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
)) as {
|
||||||
|
labels: { nodes: { name: string }[] };
|
||||||
|
number: number;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
return (
|
||||||
|
res.map((issue) => {
|
||||||
|
return {
|
||||||
|
...issue,
|
||||||
|
issueType,
|
||||||
|
labels: issue.labels.nodes,
|
||||||
|
html_url: issue.url,
|
||||||
|
};
|
||||||
|
}) ?? []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
|
export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
|
||||||
const result: RenovateOpenItems = {
|
const result: RenovateOpenItems = {
|
||||||
managers: {},
|
managers: {},
|
||||||
|
@ -59,18 +86,10 @@ export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const q = `repo:renovatebot/renovate type:issue is:open`;
|
|
||||||
const per_page = 100;
|
|
||||||
try {
|
try {
|
||||||
const query = getQueryString({ q, per_page });
|
const rawItems: ItemsEntity[] = (await getIssuesByIssueType('Bug')).concat(
|
||||||
const res = await githubApi.getJsonUnchecked<GithubApiQueryResponse>(
|
await getIssuesByIssueType('Feature'),
|
||||||
gitHubApiUrl + query,
|
|
||||||
{
|
|
||||||
paginationField: 'items',
|
|
||||||
paginate: true,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
const rawItems = res.body?.items ?? [];
|
|
||||||
|
|
||||||
result.managers = extractIssues(rawItems, 'manager:');
|
result.managers = extractIssues(rawItems, 'manager:');
|
||||||
result.platforms = extractIssues(rawItems, 'platform:');
|
result.platforms = extractIssues(rawItems, 'platform:');
|
||||||
|
@ -91,9 +110,8 @@ function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems {
|
||||||
const issuesMap: OpenItems = {};
|
const issuesMap: OpenItems = {};
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const type = item.labels
|
const type = item.issueType;
|
||||||
.find((l) => l.name.startsWith('type:'))
|
|
||||||
?.name.split(':')[1];
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -107,10 +125,10 @@ function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems {
|
||||||
issuesMap[label] = { bugs: [], features: [] };
|
issuesMap[label] = { bugs: [], features: [] };
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'bug':
|
case 'Bug':
|
||||||
issuesMap[label]?.bugs.push(item);
|
issuesMap[label]?.bugs.push(item);
|
||||||
break;
|
break;
|
||||||
case 'feature':
|
case 'Feature':
|
||||||
issuesMap[label]?.features.push(item);
|
issuesMap[label]?.features.push(item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue