import * as moment from 'moment'; import fetch from 'cross-fetch'; import { getCurrentApiBaseUrlWithAuth } from '../git/git'; type PullRequestFromApi = any; export type PullRequest = { number: number; title: string; closedIssueNumbers: number[]; headSha: string; mergeCommitSha: string; mergedAt: string; isBreakingChange: boolean; }; const PULL_REQUEST_INDEX_API_URI = getCurrentApiBaseUrlWithAuth('/pulls?state=closed'); export async function getMergedPullRequests(since: string): Promise { const list = await fetchPullRequests(since); const listMergedBefore = list.filter((pr: PullRequest): boolean => !!pr.mergedAt); return listMergedBefore; } async function fetchPullRequests(since: string): Promise { const pullRequestsSince = await fetchPullRequestsFromApi(since); return pullRequestsSince.map((pr: PullRequestFromApi): PullRequest => { const isBreakingChange = pr.labels.find((label: any): boolean => label.name.toLowerCase().includes('breaking')); return { number: pr.number, title: pr.title, closedIssueNumbers: findIssueNumbers(pr.body), mergeCommitSha: pr.merge_commit_sha, headSha: pr.head.sha, mergedAt: pr.merged_at, isBreakingChange: isBreakingChange, }; }); } async function fetchPullRequestsFromApi(since: string, page: number = 1): Promise { const response = await fetch(`${PULL_REQUEST_INDEX_API_URI}&page=${page}`); const results = (await response.json()) as PullRequestFromApi[]; const pullRequestsSince = results.filter((pr: PullRequestFromApi): boolean => moment(pr.merged_at).isAfter(since)); if (pullRequestsSince.length > 0) { const nextPagePullRequests = await fetchPullRequestsFromApi(since, page + 1); return [...pullRequestsSince].concat(nextPagePullRequests); } return pullRequestsSince; } function findIssueNumbers(body: string): number[] { if (!body || body.trim().length === 0) { return []; } const lines = body.split('\n').filter((line: string): boolean => line.match(/^(closes|fixes)/i) != null); const matchedNumbers = []; lines.forEach((line: string): void => { const matched = line.match(/(#(\d+))/gi); if (matched == null) { return; } for (const matchedString of matched) { const numberAsString = matchedString.replace('#', ''); matchedNumbers.push(parseInt(numberAsString)); } }); return matchedNumbers; }