diff --git a/src/repositories/GiteaRepository.ts b/src/repositories/GiteaRepository.ts index fcb7522..3a01ecc 100644 --- a/src/repositories/GiteaRepository.ts +++ b/src/repositories/GiteaRepository.ts @@ -1,258 +1,241 @@ -import {BaseRepository} from "./BaseRepository"; -import {TagInfo} from "../pr-collector/tags"; -import { - CommentInfo, - PullRequestInfo, -} from "../pr-collector/pullRequests"; -import {DiffInfo} from "../pr-collector/commits"; -import {Api, PullRequest, PullReview} from "gitea-js"; -import moment, {min} from "moment"; -import * as core from "@actions/core"; -import {createCommandManager} from "../pr-collector/gitHelper"; -import {match} from "assert"; -import {log} from "util"; +import {BaseRepository} from './BaseRepository' +import {TagInfo} from '../pr-collector/tags' +import {CommentInfo, PullRequestInfo} from '../pr-collector/pullRequests' +import {DiffInfo} from '../pr-collector/commits' +import {Api, PullRequest, PullReview} from 'gitea-js' +import moment from 'moment' +import * as core from '@actions/core' +import {createCommandManager} from '../pr-collector/gitHelper' +interface Pulls { + closed: PullRequest[] + open: PullRequest[] +} export class GiteaRepository extends BaseRepository { - private api: Api + private api: Api - get defaultUrl(): string { - return "https://gitea.com"; + get defaultUrl(): string { + return 'https://gitea.com' + } + + constructor(token: string, url: string | undefined, repositoryPath: string) { + super(token, url, repositoryPath) + this.url = url || this.defaultUrl + const fetch = require('cross-fetch') + const {giteaApi} = require('gitea-js') + // @ts-ignore + this.api = new giteaApi('https://gitea.com/', { + token, // generate one at https://gitea.example.com/user/settings/applications + customFetch: fetch + }) + } + + async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise { + const response = await this.api.repos.repoGetTag(owner, repo, tagInfo.name) + + if (response.error === null) { + if (response.data.commit) { + tagInfo.date = moment(response.data.commit.created) + core.info(`ℹ️ Retrieved information about the release associated with ${tagInfo.name} from the Gitea API`) + return tagInfo + } + } + return await this.getTagByCreateTime(repositoryPath, tagInfo) + } + + async getBetweenDates( + owner: string, + repo: string, + fromDate: moment.Moment, + toDate: moment.Moment, + maxPullRequests: number + ): Promise { + const mergedPRs: PullRequestInfo[] = [] + await this.getAllPullRequest(owner, repo, 'closed', maxPullRequests) + + for (const pr of GiteaRepository.pulls.closed.filter(p => !!p.merged_at)) { + if (moment(pr.closed_at) > fromDate && moment(pr.closed_at) < toDate) { + mergedPRs.push(this.mapPullRequest(pr, 'merged')) + } } - constructor(token: string, url: string | undefined, repositoryPath: string) { - super(token, url, repositoryPath); - this.url = url || this.defaultUrl - const fetch = require('cross-fetch'); - const {giteaApi} = require('gitea-js'); - this.api = new giteaApi('https://gitea.com/', { - token: token, // generate one at https://gitea.example.com/user/settings/applications - customFetch: fetch, - }); + return mergedPRs + } + + private mapPullRequest(pr: PullRequest, status: 'open' | 'merged' = 'open'): PullRequestInfo { + return { + number: pr.number || 0, + title: pr.title || '', + htmlURL: pr.html_url || '', + baseBranch: pr.base?.repo?.default_branch || '', + branch: pr.merge_base || '', + 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: pr.labels?.map(label => label.name) as string[], + milestone: pr.milestone?.title || '', + body: pr.body || '', + + assignees: pr.assignees?.map(user => user.full_name) as string[], + requestedReviewers: pr.requested_reviewers?.map(user => user.full_name) as string[], + approvedReviewers: [], + createdAt: moment(pr.created_at), + status, + reviews: undefined + } + } + + async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise { + let changedFilesCount = 0 + let additionCount = 0 + let deletionCount = 0 + const changeCount = 0 + let commitCount = 0 + + const gitHelper = await createCommandManager(this.repositoryPath) + + // Get the diff stats between the two branches/commits + const diffStat = await gitHelper.execGit(['diff', '--stat', `${base}...${head}`]) + const diffStatLines = diffStat.stdout.split('\n') + + for (const line of diffStatLines) { + // Extract the addition and deletion counts from each line of the git diff output + const match = line.match(/(\d+) insertions?\(\+\), (\d+) deletions?\(\-\)/) + if (match) { + additionCount += parseInt(match[1], 10) + deletionCount += parseInt(match[2], 10) + } } - async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise { - const response = await this.api.repos.repoGetTag(owner, repo, tagInfo.name) + // Get the list of changed files + const diffNameOnly = await gitHelper.execGit(['diff', '--name-only', `${base}...${head}`]) + const changedFiles = diffNameOnly.stdout.split('\n') + changedFilesCount = changedFiles.length - 1 // Subtract one for the empty line at the end - if (response.error === null) { + // Get the commit count between the two branches/commits + const logCount = await gitHelper.execGit(['rev-list', '--count', `${base}...${head}`]) + commitCount = parseInt(logCount.stdout.trim(), 10) - if (response.data.commit) { - tagInfo.date = moment(response.data.commit.created) - core.info(`ℹ️ Retrieved information about the release associated with ${tagInfo.name} from the Gitea API`) - return tagInfo - } + // Now let's get the commit logs between the two branches/commits + const log = await gitHelper.execGit(['log', '--pretty=format:%H||||%an||||%ae||||%ad||||%cn||||%ce||||%cd||||%s', `${base}...${head}`]) + const commitLogs = log.stdout.trim().split('\n') - - } - return await this.getTagByCreateTime(repositoryPath, tagInfo) - - } - - async getBetweenDates(owner: string, repo: string, fromDate: moment.Moment, toDate: moment.Moment, maxPullRequests: number): Promise { - - const mergedPRs: PullRequestInfo[] = [] - - const response = await this.api.repos.repoListPullRequests(owner, repo, { - sort: 'recentupdate', - state: 'closed', - limit: Math.min(100, maxPullRequests), - }) - - if (response.error === null) { - - for (const pr of response.data.filter(p => !!p.merged_at)) { - mergedPRs.push(this.mapPullRequest(pr, 'merged')) - } - } - - - return mergedPRs; - } - - private mapPullRequest(pr: PullRequest, status: 'open' | 'merged' = 'open'): PullRequestInfo { - return { - number: pr.number || 0, - title: pr.title || "", - htmlURL: pr.html_url || "", - baseBranch: pr.base?.repo?.default_branch || "", - branch: pr.merge_base || "", - 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: pr.labels?.map((label) => label.name) as string[], - milestone: pr.milestone?.title || "", - body: pr.body || "", - - assignees: pr.assignees?.map((user) => user.full_name) as string[], - requestedReviewers: pr.requested_reviewers?.map((user) => user.full_name) as string[], - approvedReviewers: [], - createdAt: moment(pr.created_at), - status: status, - reviews: undefined - } - } - - 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; - - const gitHelper = await createCommandManager(this.repositoryPath) - - // Get the diff stats between the two branches/commits - const diffStat = await gitHelper.execGit(['diff', '--stat', `${base}...${head}`]); - const diffStatLines = diffStat.stdout.split('\n'); - - for (const line of diffStatLines) { - // Extract the addition and deletion counts from each line of the git diff output - const match = line.match(/(\d+) insertions?\(\+\), (\d+) deletions?\(\-\)/); - if (match) { - additionCount += parseInt(match[1], 10); - deletionCount += parseInt(match[2], 10); - } - } - - // Get the list of changed files - const diffNameOnly = await gitHelper.execGit(['diff', '--name-only', `${base}...${head}`]); - const changedFiles = diffNameOnly.stdout.split('\n'); - changedFilesCount = changedFiles.length - 1; // Subtract one for the empty line at the end - - // Get the commit count between the two branches/commits - const logCount = await gitHelper.execGit(['rev-list', '--count', `${base}...${head}`]); - commitCount = parseInt(logCount.stdout.trim(), 10); - - // Now let's get the commit logs between the two branches/commits - const log = await gitHelper.execGit(['log', '--pretty=format:%H||||%an||||%ae||||%ad||||%cn||||%ce||||%cd||||%s', `${base}...${head}`]); - const commitLogs = log.stdout.trim().split('\n'); - - // Process commit logs - const commitInfo = commitLogs.map(log => { - const [sha, authorName, authorEmail, authorDate, committerName, committerEmail, committerDate, subject] = log.split('||||'); - return { - sha, - summary: subject, - message: '', // This would require another git command to get the full message if needed - author: authorName, - authorDate: moment(authorDate, "ddd MMM DD HH:mm:ss YYYY ZZ", false), - committer: committerName, - commitDate: moment(committerDate, "ddd MMM DD HH:mm:ss YYYY ZZ", false), - prNumber: undefined // This is not available directly from git, would require additional logic to associate commits with PRs - }; - }); - - return { - changedFiles: changedFilesCount, - additions: additionCount, - deletions: deletionCount, - changes: changeCount, - commits: commitCount, - commitInfo: commitInfo - }; - } - - static pulls:PullRequest[] = []; - - async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise { - const mergedPRs: PullRequestInfo[] = [] - - if(GiteaRepository.pulls.length===0){ - let page = 1 - let count = 0 - while (count < maxPullRequests){ - let limit = Math.min(50, maxPullRequests) - const response = await this.api.repos.repoListPullRequests(owner, repo, { - sort: 'recentupdate', - state: 'closed', - limit: limit, - page - }) - if (response.error === null) { - GiteaRepository.pulls.push(...response.data) - - } - page++; - count+= response.data.length - if(response.data.length===0)break; - } - - } - - for (const pr of GiteaRepository.pulls) { - if (pr.merge_commit_sha === commit_sha) { - mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open')) - } - } - - - - core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`) - - return mergedPRs - - - } - - async getOpen(owner: string, repo: string, maxPullRequests: number): Promise { - const openPrs: PullRequestInfo[] = [] - const response = await this.api.repos.repoListPullRequests(owner, repo, { - sort: 'recentupdate', - state: 'open', - limit: Math.min(100, maxPullRequests), - }) - if (response.error === null) { - - for (const pr of response.data) { - openPrs.push(this.mapPullRequest(pr, 'open')) - } - } - - return openPrs - } - - async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise { - - const prReviews: CommentInfo[] = [] - const response = await this.api.repos.repoListPullReviews(owner, repo, pr.number, {}) - if (response.error === null) { - - for (const comment of response.data) { - prReviews.push(this.mapComment(comment)) - } - } - pr.reviews = prReviews - } - - private mapComment = (comment: PullReview): CommentInfo => ({ - id: comment.id || 0, - htmlURL: comment.html_url || "", - submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined, - author: comment.user?.login || '', - body: comment.body || "", - state: comment.state + // Process commit logs + const commitInfo = commitLogs.map(log => { + const [sha, authorName, authorEmail, authorDate, committerName, committerEmail, committerDate, subject] = log.split('||||') + return { + sha, + summary: subject, + message: '', // This would require another git command to get the full message if needed + author: authorName, + authorDate: moment(authorDate, 'ddd MMM DD HH:mm:ss YYYY ZZ', false), + committer: committerName, + commitDate: moment(committerDate, 'ddd MMM DD HH:mm:ss YYYY ZZ', false), + prNumber: undefined // This is not available directly from git, would require additional logic to associate commits with PRs + } }) - async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise { - const response = await this.api.repos.repoListTags(owner, repo, { - limit: maxTagsToFetch + return { + changedFiles: changedFilesCount, + additions: additionCount, + deletions: deletionCount, + changes: changeCount, + commits: commitCount, + commitInfo + } + } + + static pulls: Pulls = { + closed: [], + open: [] + } + + private async getAllPullRequest(owner: string, repo: string, state: 'closed' | 'open', maxPullRequests: number) { + if (GiteaRepository.pulls[state].length === 0) { + let page = 1 + let count = 0 + while (count < maxPullRequests) { + const limit = Math.min(50, maxPullRequests) + const response = await this.api.repos.repoListPullRequests(owner, repo, { + sort: 'recentupdate', + state, + limit, + page }) - const tagsInfo: TagInfo[] = [] if (response.error === null) { - for (const tag of response.data) { - tagsInfo.push({ - name: tag.name || "", - commit: tag.commit?.sha - }) - } - + GiteaRepository.pulls[state].push(...response.data) } - core.info(`ℹ️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`) - return tagsInfo + page++ + count += response.data.length + if (response.data.length === 0) break + } + } + } + + async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise { + const mergedPRs: PullRequestInfo[] = [] + await this.getAllPullRequest(owner, repo, 'closed', maxPullRequests) + + for (const pr of GiteaRepository.pulls.closed) { + if (pr.merge_commit_sha === commit_sha) { + mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open')) + } } - get homeUrl(): string { - return "https://gitea.com"; - } + core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`) -} \ No newline at end of file + return mergedPRs + } + + async getOpen(owner: string, repo: string, maxPullRequests: number): Promise { + await this.getAllPullRequest(owner, repo, 'open', maxPullRequests) + + const openPrs: PullRequestInfo[] = [] + for (const pr of GiteaRepository.pulls.open) { + openPrs.push(this.mapPullRequest(pr, 'open')) + } + return openPrs + } + + async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise { + const prReviews: CommentInfo[] = [] + const response = await this.api.repos.repoListPullReviews(owner, repo, pr.number, {}) + if (response.error === null) { + for (const comment of response.data) { + prReviews.push(this.mapComment(comment)) + } + } + pr.reviews = prReviews + } + + private mapComment = (comment: PullReview): CommentInfo => ({ + id: comment.id || 0, + htmlURL: comment.html_url || '', + submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined, + author: comment.user?.login || '', + body: comment.body || '', + state: comment.state + }) + + async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise { + const response = await this.api.repos.repoListTags(owner, repo, { + limit: maxTagsToFetch + }) + const tagsInfo: TagInfo[] = [] + if (response.error === null) { + for (const tag of response.data) { + tagsInfo.push({ + name: tag.name || '', + commit: tag.commit?.sha + }) + } + } + core.info(`ℹ️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`) + return tagsInfo + } + + get homeUrl(): string { + return 'https://gitea.com' + } +}