From e08045ee77082f81e91e54a489f612b1b9569754 Mon Sep 17 00:00:00 2001 From: Ankio Date: Wed, 1 Nov 2023 11:05:11 +0800 Subject: [PATCH] style: code formatting --- src/configuration.ts | 2 +- src/pr-collector/commits.ts | 238 ++++++----- src/pr-collector/gitHelper.ts | 2 +- src/pr-collector/prCollector.ts | 12 +- src/pr-collector/pullRequests.ts | 23 +- src/pr-collector/tags.ts | 6 +- src/pr-collector/types.ts | 72 ++-- src/regexUtils.ts | 2 +- src/releaseNotesBuilder.ts | 3 +- src/repositories/BaseRepository.ts | 98 ++--- src/repositories/GithubRepository.ts | 575 ++++++++++++++------------- src/transform.ts | 2 +- src/utils.ts | 1 + 13 files changed, 520 insertions(+), 516 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 18b3908..c1e2b97 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,4 +1,4 @@ -import {Rule, Extractor, Regex, Transformer, Sort, PullConfiguration} from './pr-collector/types' +import {Extractor, PullConfiguration, Regex, Rule, Sort, Transformer} from './pr-collector/types' export interface Configuration extends PullConfiguration { max_tags_to_fetch: number diff --git a/src/pr-collector/commits.ts b/src/pr-collector/commits.ts index ee2a1c0..1c7643e 100644 --- a/src/pr-collector/commits.ts +++ b/src/pr-collector/commits.ts @@ -1,155 +1,153 @@ import * as core from '@actions/core' -import {Octokit, RestEndpointMethodTypes} from '@octokit/rest' import moment from 'moment' import {failOrError} from './utils' import {PullRequestInfo} from './pullRequests' import {Options} from './prCollector' -import {BaseRepository} from "../repositories/BaseRepository"; +import {BaseRepository} from '../repositories/BaseRepository' export interface DiffInfo { - changedFiles: number - additions: number - deletions: number - changes: number - commits: number - commitInfo: CommitInfo[] + changedFiles: number + additions: number + deletions: number + changes: number + commits: number + commitInfo: CommitInfo[] } export const DefaultDiffInfo: DiffInfo = { - changedFiles: 0, - additions: 0, - deletions: 0, - changes: 0, - commits: 0, - commitInfo: [] + changedFiles: 0, + additions: 0, + deletions: 0, + changes: 0, + commits: 0, + commitInfo: [] } export interface CommitInfo { - sha: string - summary: string - message: string - author: string - authorDate: moment.Moment - committer: string - commitDate: moment.Moment + sha: string + summary: string + message: string + author: string + authorDate: moment.Moment + committer: string + commitDate: moment.Moment } export class Commits { - constructor(private repositoryUtils: BaseRepository) { + constructor(private repositoryUtils: BaseRepository) {} + + async getDiff(owner: string, repo: string, base: string, head: string): Promise { + const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head) + diff.commitInfo = this.sortCommits(diff.commitInfo) + return diff + } + + private async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise { + return this.repositoryUtils.getDiffRemote(owner, repo, base, head) + } + + private sortCommits(commits: CommitInfo[]): CommitInfo[] { + const commitsResult = [] + const shas: {[key: string]: boolean} = {} + + for (const commit of commits) { + if (shas[commit.sha]) { + continue + } + shas[commit.sha] = true + commitsResult.push(commit) } - async getDiff(owner: string, repo: string, base: string, head: string): Promise { - const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head) - diff.commitInfo = this.sortCommits(diff.commitInfo) - return diff + commitsResult.sort((a, b) => { + if (a.commitDate.isBefore(b.commitDate)) { + return -1 + } else if (b.commitDate.isBefore(a.commitDate)) { + return 1 + } + return 0 + }) + + return commitsResult + } + + async getCommitHistory(options: Options): Promise { + const {owner, repo, fromTag, toTag, failOnError} = options + core.info(`ℹ️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`) + + const commitsApi = new Commits(this.repositoryUtils) + let diffInfo: DiffInfo + try { + diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name) + } catch (error) { + failOrError(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError) + return DefaultDiffInfo + } + if (diffInfo.commitInfo.length === 0) { + core.warning(`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`) + return DefaultDiffInfo } - private async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise { - return this.repositoryUtils.getDiffRemote(owner, repo, base, head) + return diffInfo + } + + async generateCommitPRs(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> { + const {owner, repo, configuration} = options + + const diffInfo = await this.getCommitHistory(options) + const commits = diffInfo.commitInfo + if (commits.length === 0) { + return [diffInfo, []] } - private sortCommits(commits: CommitInfo[]): CommitInfo[] { - const commitsResult = [] - const shas: { [key: string]: boolean } = {} + const prCommits = filterCommits(commits, configuration.exclude_merge_branches) - for (const commit of commits) { - if (shas[commit.sha]) { - continue - } - shas[commit.sha] = true - commitsResult.push(commit) - } + core.info(`ℹ️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`) - commitsResult.sort((a, b) => { - if (a.commitDate.isBefore(b.commitDate)) { - return -1 - } else if (b.commitDate.isBefore(a.commitDate)) { - return 1 - } - return 0 - }) - - return commitsResult - } - - async getCommitHistory(options: Options): Promise { - const {owner, repo, fromTag, toTag, failOnError} = options - core.info(`ℹ️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`) - - const commitsApi = new Commits(this.repositoryUtils) - let diffInfo: DiffInfo - try { - diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name) - } catch (error) { - failOrError(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError) - return DefaultDiffInfo - } - if (diffInfo.commitInfo.length === 0) { - core.warning(`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`) - return DefaultDiffInfo - } - - return diffInfo - } - - async generateCommitPRs(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> { - const {owner, repo, configuration} = options - - const diffInfo = await this.getCommitHistory(options) - const commits = diffInfo.commitInfo - if (commits.length === 0) { - return [diffInfo, []] - } - - const prCommits = filterCommits(commits, configuration.exclude_merge_branches) - - core.info(`ℹ️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`) - - const prs = prCommits.map(function (commit): PullRequestInfo { - return { - number: 0, - title: commit.summary, - htmlURL: '', - baseBranch: '', - createdAt: commit.commitDate, - mergedAt: commit.commitDate, - mergeCommitSha: commit.sha, - author: commit.author || '', - repoName: '', - labels: [], - milestone: '', - body: commit.message || '', - assignees: [], - requestedReviewers: [], - approvedReviewers: [], - status: 'merged' - } - }) - return [diffInfo, prs] - } + const prs = prCommits.map(function (commit): PullRequestInfo { + return { + number: 0, + title: commit.summary, + htmlURL: '', + baseBranch: '', + createdAt: commit.commitDate, + mergedAt: commit.commitDate, + mergeCommitSha: commit.sha, + author: commit.author || '', + repoName: '', + labels: [], + milestone: '', + body: commit.message || '', + assignees: [], + requestedReviewers: [], + approvedReviewers: [], + status: 'merged' + } + }) + return [diffInfo, prs] + } } /** * Filters out all commits which match the exclude pattern */ export function filterCommits(commits: CommitInfo[], excludeMergeBranches: string[]): CommitInfo[] { - const filteredCommits = [] + const filteredCommits = [] - for (const commit of commits) { - if (excludeMergeBranches) { - let matched = false - for (const excludeMergeBranch of excludeMergeBranches) { - if (commit.summary.includes(excludeMergeBranch)) { - matched = true - break - } - } - if (matched) { - continue - } + for (const commit of commits) { + if (excludeMergeBranches) { + let matched = false + for (const excludeMergeBranch of excludeMergeBranches) { + if (commit.summary.includes(excludeMergeBranch)) { + matched = true + break } - filteredCommits.push(commit) + } + if (matched) { + continue + } } + filteredCommits.push(commit) + } - return filteredCommits + return filteredCommits } diff --git a/src/pr-collector/gitHelper.ts b/src/pr-collector/gitHelper.ts index 035ebd8..84d2c50 100644 --- a/src/pr-collector/gitHelper.ts +++ b/src/pr-collector/gitHelper.ts @@ -39,7 +39,7 @@ class GitCommandManager { return result } - async execGit(args: string[], allowAllExitCodes = false, silent = false): Promise { + async execGit(args: string[], allowAllExitCodes = false, silent = false): Promise { directoryExistsSync(this.workingDirectory, true) const result = new GitOutput() diff --git a/src/pr-collector/prCollector.ts b/src/pr-collector/prCollector.ts index f81a61e..e7300e7 100644 --- a/src/pr-collector/prCollector.ts +++ b/src/pr-collector/prCollector.ts @@ -1,12 +1,10 @@ import * as core from '@actions/core' import {PullConfiguration} from './types' -import {Octokit} from '@octokit/rest' import {TagInfo, Tags} from './tags' import {failOrError} from './utils' -import {HttpsProxyAgent} from 'https-proxy-agent' import {PullRequestInfo, PullRequests} from './pullRequests' import {Commits, DiffInfo} from './commits' -import {BaseRepository} from "../repositories/BaseRepository"; +import {BaseRepository} from '../repositories/BaseRepository' export interface Options { owner: string // the owner of the repository @@ -33,7 +31,7 @@ export interface Data { export class PullRequestCollector { constructor( private baseUrl: string | null, - private repositoryUtils: BaseRepository , + private repositoryUtils: BaseRepository, private repositoryPath: string, private owner: string, private repo: string, @@ -47,7 +45,7 @@ export class PullRequestCollector { private fetchReleaseInformation = false, private fetchReviews = false, private commitMode = false, - private configuration: PullConfiguration, + private configuration: PullConfiguration ) {} async build(): Promise { @@ -93,7 +91,7 @@ export class PullRequestCollector { core.endGroup() - return await pullData( this.repositoryUtils, { + return await pullData(this.repositoryUtils, { owner: this.owner, repo: this.repo, fromTag: previousTag, @@ -110,7 +108,7 @@ export class PullRequestCollector { } } -export async function pullData( repositoryUtils: BaseRepository , options: Options): Promise { +export async function pullData(repositoryUtils: BaseRepository, options: Options): Promise { let mergedPullRequests: PullRequestInfo[] let diffInfo: DiffInfo diff --git a/src/pr-collector/pullRequests.ts b/src/pr-collector/pullRequests.ts index 86a9fca..2bb4210 100644 --- a/src/pr-collector/pullRequests.ts +++ b/src/pr-collector/pullRequests.ts @@ -1,11 +1,10 @@ import * as core from '@actions/core' -import {Octokit, RestEndpointMethodTypes} from '@octokit/rest' -import {Unpacked} from './utils' +import {RestEndpointMethodTypes} from '@octokit/rest' import moment from 'moment' import {Property, Sort} from './types' import {Commits, DiffInfo, filterCommits} from './commits' import {Options} from './prCollector' -import {BaseRepository} from "../repositories/BaseRepository"; +import {BaseRepository} from '../repositories/BaseRepository' export interface PullRequestInfo { number: number @@ -65,7 +64,7 @@ export const EMPTY_COMMENT_INFO: CommentInfo = { state: undefined } -export type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data'] +export type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data'] export type PullsListData = RestEndpointMethodTypes['pulls']['list']['response']['data'] @@ -77,17 +76,22 @@ export class PullRequests { private commits: Commits ) {} - async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise { return sortPrs(await this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests)) } - async getBetweenDates(owner: string, repo: string, fromDate: moment.Moment, toDate: moment.Moment, maxPullRequests: number): Promise { - return sortPrs(await this.repositoryUtils.getBetweenDates(owner, repo,fromDate,toDate,maxPullRequests)) + async getBetweenDates( + owner: string, + repo: string, + fromDate: moment.Moment, + toDate: moment.Moment, + maxPullRequests: number + ): Promise { + return sortPrs(await this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests)) } async getOpen(owner: string, repo: string, maxPullRequests: number): Promise { - return sortPrs(await this.repositoryUtils.getOpen(owner, repo,maxPullRequests)) + return sortPrs(await this.repositoryUtils.getOpen(owner, repo, maxPullRequests)) } async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise { @@ -215,8 +219,6 @@ export class PullRequests { } } - - function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] { return sortPullRequests(pullRequests, { order: 'ASC', @@ -281,4 +283,3 @@ export function retrieveProperty(pr: PullRequestInfo, property: Property, useCas } return value } - diff --git a/src/pr-collector/tags.ts b/src/pr-collector/tags.ts index 03635c7..ac80f97 100644 --- a/src/pr-collector/tags.ts +++ b/src/pr-collector/tags.ts @@ -6,7 +6,7 @@ import {RegexTransformer, TagResolver, Transformer} from './types' import {createCommandManager} from './gitHelper' import moment from 'moment' import {validateTransformer} from './regexUtils' -import {BaseRepository} from "../repositories/BaseRepository"; +import {BaseRepository} from '../repositories/BaseRepository' export interface TagResult { from: TagInfo | null @@ -28,11 +28,11 @@ export class Tags { constructor(private repositoryUtils: BaseRepository) {} async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise { - return this.repositoryUtils.getTags(owner,repo,maxTagsToFetch) + return this.repositoryUtils.getTags(owner, repo, maxTagsToFetch) } async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise { - return this.repositoryUtils.fillTagInformation(repositoryPath,owner,repo,tagInfo) + return this.repositoryUtils.fillTagInformation(repositoryPath, owner, repo, tagInfo) } async findPredecessorTag( diff --git a/src/pr-collector/types.ts b/src/pr-collector/types.ts index 6166942..dd17cfa 100644 --- a/src/pr-collector/types.ts +++ b/src/pr-collector/types.ts @@ -1,63 +1,63 @@ export interface PullConfiguration { - max_tags_to_fetch: number - max_pull_requests: number - max_back_track_time_days: number - exclude_merge_branches: string[] - sort: Sort | string // "ASC" or "DESC" - tag_resolver: TagResolver - base_branches: string[] + max_tags_to_fetch: number + max_pull_requests: number + max_back_track_time_days: number + exclude_merge_branches: string[] + sort: Sort | string // "ASC" or "DESC" + tag_resolver: TagResolver + base_branches: string[] } /** * Defines the properties of the PullRequestInfo useable in different configurations */ export type Property = - | 'number' - | 'title' - | 'branch' - | 'author' - | 'labels' - | 'milestone' - | 'body' - | 'assignees' - | 'requestedReviewers' - | 'approvedReviewers' - | 'status' + | 'number' + | 'title' + | 'branch' + | 'author' + | 'labels' + | 'milestone' + | 'body' + | 'assignees' + | 'requestedReviewers' + | 'approvedReviewers' + | 'status' export interface Rule extends Regex { - on_property?: Property // retrieve the property to apply the rule on + on_property?: Property // retrieve the property to apply the rule on } export interface Sort { - order: 'ASC' | 'DESC' // the sorting order - on_property: 'mergedAt' | 'title' // the property to sort on. (mergedAt falls back to createdAt) + order: 'ASC' | 'DESC' // the sorting order + on_property: 'mergedAt' | 'title' // the property to sort on. (mergedAt falls back to createdAt) } export interface TagResolver { - method: string // semver, sort - filter?: Regex // the regex to filter the tags, prior to sorting - transformer?: Transformer | Transformer[] // transforms the tag name using the regex, run after the filter + method: string // semver, sort + filter?: Regex // the regex to filter the tags, prior to sorting + transformer?: Transformer | Transformer[] // transforms the tag name using the regex, run after the filter } export interface Regex { - pattern: string // the regex pattern to match - flags?: string // the regex flag to use for RegExp + pattern: string // the regex pattern to match + flags?: string // the regex flag to use for RegExp } export interface Transformer extends Regex { - target?: string // the target string to transform the source string using the regex to + target?: string // the target string to transform the source string using the regex to } export interface Extractor extends Transformer { - on_property?: Property[] | Property | undefined // retrieve the property to extract the value from - method?: 'replace' | 'match' | undefined // the method to use to extract the value, `match` will not use the `target` property - on_empty?: string | undefined // in case the regex results in an empty string, this value is gonna be used instead (only for label_extractor currently) + on_property?: Property[] | Property | undefined // retrieve the property to extract the value from + method?: 'replace' | 'match' | undefined // the method to use to extract the value, `match` will not use the `target` property + on_empty?: string | undefined // in case the regex results in an empty string, this value is gonna be used instead (only for label_extractor currently) } export interface RegexTransformer { - pattern: RegExp | null - target: string - onProperty?: Property[] - method?: 'replace' | 'match' - onEmpty?: string -} \ No newline at end of file + pattern: RegExp | null + target: string + onProperty?: Property[] + method?: 'replace' | 'match' + onEmpty?: string +} diff --git a/src/regexUtils.ts b/src/regexUtils.ts index b79251d..2cd3a03 100644 --- a/src/regexUtils.ts +++ b/src/regexUtils.ts @@ -1,5 +1,5 @@ import * as core from '@actions/core' -import {Rule, RegexTransformer} from './pr-collector/types' +import {RegexTransformer, Rule} from './pr-collector/types' import {PullRequestInfo, retrieveProperty} from './pr-collector/pullRequests' import {validateTransformer} from './pr-collector/regexUtils' diff --git a/src/releaseNotesBuilder.ts b/src/releaseNotesBuilder.ts index 176aace..4350044 100644 --- a/src/releaseNotesBuilder.ts +++ b/src/releaseNotesBuilder.ts @@ -1,13 +1,12 @@ import * as core from '@actions/core' import {Configuration} from './configuration' import {checkExportedData, writeCacheData} from './utils' -import {PullRequestData, buildChangelog} from './transform' +import {buildChangelog, PullRequestData} from './transform' import {PullRequestCollector} from './pr-collector/prCollector' import {failOrError} from './pr-collector/utils' import {TagInfo} from './pr-collector/tags' import {DiffInfo} from './pr-collector/commits' import {PullRequestInfo} from './pr-collector/pullRequests' -import * as fs from 'fs' import {BaseRepository} from './repositories/BaseRepository' export interface ReleaseNotesOptions { diff --git a/src/repositories/BaseRepository.ts b/src/repositories/BaseRepository.ts index f31dd35..68eadf5 100644 --- a/src/repositories/BaseRepository.ts +++ b/src/repositories/BaseRepository.ts @@ -1,60 +1,66 @@ -import {TagInfo} from "../pr-collector/tags"; -import {DiffInfo} from "../pr-collector/commits"; -import {Options} from "../pr-collector/prCollector"; -import moment from "moment/moment"; -import {PullRequestInfo} from "../pr-collector/pullRequests"; -import * as core from "@actions/core"; -import {createCommandManager} from "../pr-collector/gitHelper"; +import {TagInfo} from '../pr-collector/tags' +import {DiffInfo} from '../pr-collector/commits' +import moment from 'moment/moment' +import {PullRequestInfo} from '../pr-collector/pullRequests' +import * as core from '@actions/core' +import {createCommandManager} from '../pr-collector/gitHelper' export abstract class BaseRepository { - proxy?: string; - noProxyArray: string[] + proxy?: string + noProxyArray: string[] - // Define an abstract getter for the default URL - abstract get defaultUrl(): string; + // Define an abstract getter for the default URL + abstract get defaultUrl(): string - abstract get homeUrl():string; - protected constructor(protected token: string, protected url: string | undefined,protected repositoryPath:string) { - this.proxy = process.env.https_proxy || process.env.HTTPS_PROXY - const noProxy = process.env.no_proxy || process.env.NO_PROXY - this.noProxyArray = [] - if (noProxy) { - this.noProxyArray = noProxy.split(',') - } + abstract get homeUrl(): string + protected constructor( + protected token: string, + protected url: string | undefined, + protected repositoryPath: string + ) { + this.proxy = process.env.https_proxy || process.env.HTTPS_PROXY + const noProxy = process.env.no_proxy || process.env.NO_PROXY + this.noProxyArray = [] + if (noProxy) { + this.noProxyArray = noProxy.split(',') } + } - abstract getTags(owner: string, repo: string, maxTagsToFetch: number): Promise + abstract getTags(owner: string, repo: string, maxTagsToFetch: number): Promise - abstract fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise + abstract fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise - abstract getDiffRemote(owner: string, repo: string, base: string, head: string): Promise + abstract getDiffRemote(owner: string, repo: string, base: string, head: string): Promise + abstract getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise - abstract getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise + abstract getBetweenDates( + owner: string, + repo: string, + fromDate: moment.Moment, + toDate: moment.Moment, + maxPullRequests: number + ): Promise - abstract getBetweenDates(owner: string, repo: string, fromDate: moment.Moment, toDate: moment.Moment, maxPullRequests: number): Promise + abstract getOpen(owner: string, repo: string, maxPullRequests: number): Promise - abstract getOpen(owner: string, repo: string, maxPullRequests: number): Promise + abstract getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise - abstract getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise - - - protected async getTagByCreateTime(repositoryPath: string, tagInfo: TagInfo) { - core.info(`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`) - const gitHelper = await createCommandManager(repositoryPath) - const creationTimeString = await gitHelper.tagCreation(tagInfo.name) - const creationTime = moment(creationTimeString) - if (creationTimeString !== null && creationTime.isValid()) { - tagInfo.date = creationTime - core.info( - `ℹ️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}` - ) - } else { - core.info( - `⚠️ Could not retrieve tag creation time via git cli 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}'` - ) - } - return tagInfo + protected async getTagByCreateTime(repositoryPath: string, tagInfo: TagInfo) { + core.info(`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`) + const gitHelper = await createCommandManager(repositoryPath) + const creationTimeString = await gitHelper.tagCreation(tagInfo.name) + const creationTime = moment(creationTimeString) + if (creationTimeString !== null && creationTime.isValid()) { + tagInfo.date = creationTime + core.info( + `ℹ️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}` + ) + } else { + core.info( + `⚠️ Could not retrieve tag creation time via git cli 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}'` + ) } - -} \ No newline at end of file + return tagInfo + } +} diff --git a/src/repositories/GithubRepository.ts b/src/repositories/GithubRepository.ts index 8b4303f..750678e 100644 --- a/src/repositories/GithubRepository.ts +++ b/src/repositories/GithubRepository.ts @@ -1,302 +1,303 @@ -import {BaseRepository} from "./BaseRepository"; -import {Octokit, RestEndpointMethodTypes} from "@octokit/rest"; -import {HttpsProxyAgent} from "https-proxy-agent"; -import * as core from "@actions/core"; -import {TagInfo} from "../pr-collector/tags"; -import moment from "moment/moment"; -import {DiffInfo} from "../pr-collector/commits"; -import { - CommentInfo, PullData, - PullRequestInfo, - PullReviewsData, PullsListData -} from "../pr-collector/pullRequests"; -import {Unpacked} from "../pr-collector/utils"; +import {BaseRepository} from './BaseRepository' +import {Octokit, RestEndpointMethodTypes} from '@octokit/rest' +import {HttpsProxyAgent} from 'https-proxy-agent' +import * as core from '@actions/core' +import {TagInfo} from '../pr-collector/tags' +import moment from 'moment/moment' +import {DiffInfo} from '../pr-collector/commits' +import {CommentInfo, PullData, PullRequestInfo, PullReviewsData, PullsListData} from '../pr-collector/pullRequests' +import {Unpacked} from '../pr-collector/utils' export class GithubRepository extends BaseRepository { + async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise { + let changedFilesCount = 0 + let additionCount = 0 + let deletionCount = 0 + let changeCount = 0 + let commitCount = 0 - async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise { - let changedFilesCount = 0 - let additionCount = 0 - let deletionCount = 0 - let changeCount = 0 - let commitCount = 0 - - // Fetch comparisons recursively until we don't find any commits - // This is because the GitHub API limits the number of commits returned in a single response. - let commits: RestEndpointMethodTypes['repos']['compareCommits']['response']['data']['commits'] = [] - let compareHead = head - // eslint-disable-next-line no-constant-condition - while (true) { - const compareResult = await this.octokit.repos.compareCommits({ - owner, - repo, - base, - head: compareHead - }) - if (compareResult.data.total_commits === 0) { - break - } - changedFilesCount += compareResult.data.files?.length ?? 0 - const files = compareResult.data.files - if (files !== undefined) { - for (const file of files) { - additionCount += file.additions - deletionCount += file.deletions - changeCount += file.changes - } - } - commitCount += compareResult.data.commits.length - commits = compareResult.data.commits.concat(commits) - compareHead = `${commits[0].sha}^` - } - - core.info(`ℹ️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`) - - return { - changedFiles: changedFilesCount, - additions: additionCount, - deletions: deletionCount, - changes: changeCount, - commits: commitCount, - commitInfo: commits - .filter(commit => commit.sha) - .map(commit => ({ - sha: commit.sha || '', - summary: commit.commit.message.split('\n')[0], - message: commit.commit.message, - author: commit.author?.login || '', - authorDate: moment(commit.commit.author?.date), - committer: commit.committer?.login || '', - commitDate: moment(commit.commit.committer?.date), - prNumber: undefined - })) + // Fetch comparisons recursively until we don't find any commits + // This is because the GitHub API limits the number of commits returned in a single response. + let commits: RestEndpointMethodTypes['repos']['compareCommits']['response']['data']['commits'] = [] + let compareHead = head + // eslint-disable-next-line no-constant-condition + while (true) { + const compareResult = await this.octokit.repos.compareCommits({ + owner, + repo, + base, + head: compareHead + }) + if (compareResult.data.total_commits === 0) { + break + } + changedFilesCount += compareResult.data.files?.length ?? 0 + const files = compareResult.data.files + if (files !== undefined) { + for (const file of files) { + additionCount += file.additions + deletionCount += file.deletions + changeCount += file.changes } + } + commitCount += compareResult.data.commits.length + commits = compareResult.data.commits.concat(commits) + compareHead = `${commits[0].sha}^` } - async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise { - const mergedPRs: PullRequestInfo[] = [] - const options = this.octokit.repos.listPullRequestsAssociatedWithCommit.endpoint.merge({ - owner, - repo, - commit_sha, - per_page: `${Math.min(10, maxPullRequests)}`, - direction: 'desc' - }) + core.info(`ℹ️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`) - for await (const response of this.octokit.paginate.iterator(options)) { - const prs: PullsListData = response.data as PullsListData - - for (const pr of prs) { - mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open')) - } - } - return mergedPRs + return { + changedFiles: changedFilesCount, + additions: additionCount, + deletions: deletionCount, + changes: changeCount, + commits: commitCount, + commitInfo: commits + .filter(commit => commit.sha) + .map(commit => ({ + sha: commit.sha || '', + summary: commit.commit.message.split('\n')[0], + message: commit.commit.message, + author: commit.author?.login || '', + authorDate: moment(commit.commit.author?.date), + committer: commit.committer?.login || '', + commitDate: moment(commit.commit.committer?.date), + prNumber: undefined + })) } + } - async getBetweenDates(owner: string, repo: string, fromDate: moment.Moment, toDate: moment.Moment, maxPullRequests: number): Promise { - const mergedPRs: PullRequestInfo[] = [] - const options = this.octokit.pulls.list.endpoint.merge({ - owner, - repo, - state: 'closed', - sort: 'merged', - per_page: `${Math.min(100, maxPullRequests)}`, - direction: 'desc' - }) - for await (const response of this.octokit.paginate.iterator(options)) { - const prs: PullsListData = response.data as PullsListData - - for (const pr of prs.filter(p => !!p.merged_at)) { - mergedPRs.push(this.mapPullRequest(pr, 'merged')) - } - if (mergedPRs.length >= maxPullRequests) { - core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (1)`) - break // bail out early to not keep iterating forever - } else if (prs.length > 0) { - if (this.fetchedEnough(prs, fromDate)) { - return mergedPRs // bail out early to not keep iterating on PRs super old - } - } else { - core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`) - break - } - } - return mergedPRs - } - - async getOpen(owner: string, repo: string, maxPullRequests: number): Promise { - const openPrs: PullRequestInfo[] = [] - const options = this.octokit.pulls.list.endpoint.merge({ - owner, - repo, - state: 'open', - sort: 'created', - per_page: '100', - direction: 'desc' - }) - - for await (const response of this.octokit.paginate.iterator(options)) { - const prs: PullsListData = response.data as PullsListData - - for (const pr of prs) { - openPrs.push(this.mapPullRequest(pr, 'open')) - } - - const firstPR = prs[0] - if (firstPR === undefined || openPrs.length >= maxPullRequests) { - if (openPrs.length >= maxPullRequests) { - core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (2)`) - } - break // bail out early to not keep iterating forever - } - } - return openPrs - } - - async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise { - const options = this.octokit.pulls.listReviews.endpoint.merge({ - owner, - repo, - pull_number: pr.number, - sort: 'created', - direction: 'desc' - }) - const prReviews: CommentInfo[] = [] - for await (const response of this.octokit.paginate.iterator(options)) { - const comments: PullReviewsData = response.data as PullReviewsData - - for (const comment of comments) { - prReviews.push(this.mapComment(comment)) - } - } - pr.reviews = prReviews - } - - get defaultUrl(): string { - return "https://api.github.com"; - } - - private octokit: Octokit - - constructor(token: string, url: string|undefined,repositoryPath:string) { - super(token, url,repositoryPath); - this.url = url || this.defaultUrl - - // load octokit instance - this.octokit = new Octokit({ - auth: `token ${this.token}`, - baseUrl: this.url - }) - if (this.proxy) { - const agent = new HttpsProxyAgent(this.proxy) - this.octokit.hook.before('request', options => { - if (this.noProxyArray.includes(options.request.hostname)) { - return - } - options.request.agent = agent - }) - } - } - - async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise { - const tagsInfo: TagInfo[] = [] - const options = this.octokit.repos.listTags.endpoint.merge({ - owner, - repo, - direction: 'desc', - per_page: 100 - }) - - for await (const response of this.octokit.paginate.iterator(options)) { - type TagsListData = RestEndpointMethodTypes['repos']['listTags']['response']['data'] - const tags: TagsListData = response.data as TagsListData - - for (const tag of tags) { - tagsInfo.push({ - name: tag.name, - commit: tag.commit.sha - }) - } - - // for performance only fetch newest maxTagsToFetch tags!! - if (tagsInfo.length >= maxTagsToFetch) { - break - } - } - - core.info(`ℹ️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`) - return tagsInfo - } - - async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise { - const options = this.octokit.repos.getReleaseByTag.endpoint.merge({ - owner, - repo, - tag: tagInfo.name - }) - - try { - const response = await this.octokit.request(options) - type ReleaseInformation = RestEndpointMethodTypes['repos']['getReleaseByTag']['response']['data'] - - const release: ReleaseInformation = response.data as ReleaseInformation - tagInfo.date = moment(release.created_at) - core.info(`ℹ️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API`) - } catch (error) { - tagInfo = await this.getTagByCreateTime(repositoryPath, tagInfo) - } - return tagInfo - } - -// helper function to add a special open label to prs not merged. - private attachSpecialLabels(status: 'open' | 'merged', labels: string[]): string[] { - labels.push(`--rcba-${status}`) - return labels - } - - private mapPullRequest = (pr: PullData | Unpacked, status: 'open' | 'merged' = 'open'): PullRequestInfo => ({ - number: pr.number, - title: pr.title, - htmlURL: pr.html_url, - baseBranch: pr.base.ref, - branch: pr.head.ref, - createdAt: moment(pr.created_at), - mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined, - mergeCommitSha: pr.merge_commit_sha || '', - author: pr.user?.login || '', - repoName: pr.base.repo.full_name, - labels: this.attachSpecialLabels(status, pr.labels?.map(lbl => lbl.name?.toLocaleLowerCase('en') || '') || []), - milestone: pr.milestone?.title || '', - body: pr.body || '', - assignees: pr.assignees?.map(asignee => asignee?.login || '') || [], - requestedReviewers: pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || [], - approvedReviewers: [], - reviews: undefined, - status + async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise { + const mergedPRs: PullRequestInfo[] = [] + const options = this.octokit.repos.listPullRequestsAssociatedWithCommit.endpoint.merge({ + owner, + repo, + commit_sha, + per_page: `${Math.min(10, maxPullRequests)}`, + direction: 'desc' }) - private mapComment = (comment: Unpacked): CommentInfo => ({ - id: comment.id, - htmlURL: comment.html_url, - submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined, - author: comment.user?.login || '', - body: comment.body, - state: comment.state + for await (const response of this.octokit.paginate.iterator(options)) { + const prs: PullsListData = response.data as PullsListData + + for (const pr of prs) { + mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open')) + } + } + return mergedPRs + } + + async getBetweenDates( + owner: string, + repo: string, + fromDate: moment.Moment, + toDate: moment.Moment, + maxPullRequests: number + ): Promise { + const mergedPRs: PullRequestInfo[] = [] + const options = this.octokit.pulls.list.endpoint.merge({ + owner, + repo, + state: 'closed', + sort: 'merged', + per_page: `${Math.min(100, maxPullRequests)}`, + direction: 'desc' }) - private fetchedEnough(pullRequests: PullsListData, fromDate: moment.Moment): boolean { - for (let i = 0; i < Math.min(pullRequests.length, 3); i++) { - const firstPR = pullRequests[i] - if (!firstPR.merged_at) { - // no merged_at timestamp -> look for the next - } else if (fromDate.isAfter(moment(firstPR.merged_at))) { - return true - } else { - break // not enough PRs yet, go further - } + for await (const response of this.octokit.paginate.iterator(options)) { + const prs: PullsListData = response.data as PullsListData + + for (const pr of prs.filter(p => !!p.merged_at)) { + mergedPRs.push(this.mapPullRequest(pr, 'merged')) + } + if (mergedPRs.length >= maxPullRequests) { + core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (1)`) + break // bail out early to not keep iterating forever + } else if (prs.length > 0) { + if (this.fetchedEnough(prs, fromDate)) { + return mergedPRs // bail out early to not keep iterating on PRs super old } - return false + } else { + core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`) + break + } } - get homeUrl(): string { - return "https://github.com"; + return mergedPRs + } + + async getOpen(owner: string, repo: string, maxPullRequests: number): Promise { + const openPrs: PullRequestInfo[] = [] + const options = this.octokit.pulls.list.endpoint.merge({ + owner, + repo, + state: 'open', + sort: 'created', + per_page: '100', + direction: 'desc' + }) + + for await (const response of this.octokit.paginate.iterator(options)) { + const prs: PullsListData = response.data as PullsListData + + for (const pr of prs) { + openPrs.push(this.mapPullRequest(pr, 'open')) + } + + const firstPR = prs[0] + if (firstPR === undefined || openPrs.length >= maxPullRequests) { + if (openPrs.length >= maxPullRequests) { + core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (2)`) + } + break // bail out early to not keep iterating forever + } } -} \ No newline at end of file + return openPrs + } + + async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise { + const options = this.octokit.pulls.listReviews.endpoint.merge({ + owner, + repo, + pull_number: pr.number, + sort: 'created', + direction: 'desc' + }) + const prReviews: CommentInfo[] = [] + for await (const response of this.octokit.paginate.iterator(options)) { + const comments: PullReviewsData = response.data as PullReviewsData + + for (const comment of comments) { + prReviews.push(this.mapComment(comment)) + } + } + pr.reviews = prReviews + } + + get defaultUrl(): string { + return 'https://api.github.com' + } + + private octokit: Octokit + + constructor(token: string, url: string | undefined, repositoryPath: string) { + super(token, url, repositoryPath) + this.url = url || this.defaultUrl + + // load octokit instance + this.octokit = new Octokit({ + auth: `token ${this.token}`, + baseUrl: this.url + }) + if (this.proxy) { + const agent = new HttpsProxyAgent(this.proxy) + this.octokit.hook.before('request', options => { + if (this.noProxyArray.includes(options.request.hostname)) { + return + } + options.request.agent = agent + }) + } + } + + async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise { + const tagsInfo: TagInfo[] = [] + const options = this.octokit.repos.listTags.endpoint.merge({ + owner, + repo, + direction: 'desc', + per_page: 100 + }) + + for await (const response of this.octokit.paginate.iterator(options)) { + type TagsListData = RestEndpointMethodTypes['repos']['listTags']['response']['data'] + const tags: TagsListData = response.data as TagsListData + + for (const tag of tags) { + tagsInfo.push({ + name: tag.name, + commit: tag.commit.sha + }) + } + + // for performance only fetch newest maxTagsToFetch tags!! + if (tagsInfo.length >= maxTagsToFetch) { + break + } + } + + core.info(`ℹ️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`) + return tagsInfo + } + + async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise { + const options = this.octokit.repos.getReleaseByTag.endpoint.merge({ + owner, + repo, + tag: tagInfo.name + }) + + try { + const response = await this.octokit.request(options) + type ReleaseInformation = RestEndpointMethodTypes['repos']['getReleaseByTag']['response']['data'] + + const release: ReleaseInformation = response.data as ReleaseInformation + tagInfo.date = moment(release.created_at) + core.info(`ℹ️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API`) + } catch (error) { + tagInfo = await this.getTagByCreateTime(repositoryPath, tagInfo) + } + return tagInfo + } + + // helper function to add a special open label to prs not merged. + private attachSpecialLabels(status: 'open' | 'merged', labels: string[]): string[] { + labels.push(`--rcba-${status}`) + return labels + } + + private mapPullRequest = (pr: PullData | Unpacked, status: 'open' | 'merged' = 'open'): PullRequestInfo => ({ + number: pr.number, + title: pr.title, + htmlURL: pr.html_url, + baseBranch: pr.base.ref, + branch: pr.head.ref, + createdAt: moment(pr.created_at), + mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined, + mergeCommitSha: pr.merge_commit_sha || '', + author: pr.user?.login || '', + repoName: pr.base.repo.full_name, + labels: this.attachSpecialLabels(status, pr.labels?.map(lbl => lbl.name?.toLocaleLowerCase('en') || '') || []), + milestone: pr.milestone?.title || '', + body: pr.body || '', + assignees: pr.assignees?.map(asignee => asignee?.login || '') || [], + requestedReviewers: pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || [], + approvedReviewers: [], + reviews: undefined, + status + }) + + private mapComment = (comment: Unpacked): CommentInfo => ({ + id: comment.id, + htmlURL: comment.html_url, + submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined, + author: comment.user?.login || '', + body: comment.body, + state: comment.state + }) + private fetchedEnough(pullRequests: PullsListData, fromDate: moment.Moment): boolean { + for (let i = 0; i < Math.min(pullRequests.length, 3); i++) { + const firstPR = pullRequests[i] + if (!firstPR.merged_at) { + // no merged_at timestamp -> look for the next + } else if (fromDate.isAfter(moment(firstPR.merged_at))) { + return true + } else { + break // not enough PRs yet, go further + } + } + return false + } + get homeUrl(): string { + return 'https://github.com' + } +} diff --git a/src/transform.ts b/src/transform.ts index 13ab199..fefcd07 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -11,7 +11,7 @@ import { } from './pr-collector/pullRequests' import {DiffInfo} from './pr-collector/commits' import {validateTransformer} from './pr-collector/regexUtils' -import {Transformer, RegexTransformer} from './pr-collector/types' +import {RegexTransformer, Transformer} from './pr-collector/types' import {ReleaseNotesOptions} from './releaseNotesBuilder' import {matchesRules} from './regexUtils' diff --git a/src/utils.ts b/src/utils.ts index 0e4665f..f6e58f3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,6 +7,7 @@ import {DiffInfo} from './pr-collector/commits' import {PullRequestInfo} from './pr-collector/pullRequests' import {Data, ReleaseNotesOptions} from './releaseNotesBuilder' import {env} from 'process' + /** * Resolves the repository path, relatively to the GITHUB_WORKSPACE */