- introduce new additional placeholders useable for building the changelog

| **Placeholder**            | **Description**                                                                                    | **Empty** |
|----------------------------|----------------------------------------------------------------------------------------------------|:---------:|
| `${{CHANGED_FILES}}`       | The count of changed files.                                                                        |           |
| `${{ADDITIONS}}`           | The count of code additions (lines).                                                               |           |
| `${{DELETIONS}}`           | The count of code deletions (lines).                                                               |           |
| `${{CHANGES}}`             | The count of total changes (lines).                                                                |           |
| `${{COMMITS}}`             | The count of commits in this release.                                                              |           |

- restructure project to return additional diff related information along the individual commits
- update testcases to verify functionality of new placeholders
This commit is contained in:
Mike Penz
2022-07-26 14:14:16 +02:00
parent 2b8bedc47a
commit 55354469a8
11 changed files with 245 additions and 108 deletions
+56 -19
View File
@@ -2,6 +2,24 @@ import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import moment from 'moment'
export interface DiffInfo {
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: []
}
export interface CommitInfo {
sha: string
summary: string
@@ -18,14 +36,10 @@ export class Commits {
repo: string,
base: string,
head: string
): Promise<CommitInfo[]> {
const commits: CommitInfo[] = await this.getDiffRemote(
owner,
repo,
base,
head
)
return this.sortCommits(commits)
): Promise<DiffInfo> {
const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head)
diff.commitInfo = this.sortCommits(diff.commitInfo)
return diff
}
private async getDiffRemote(
@@ -33,7 +47,13 @@ export class Commits {
repo: string,
base: string,
head: string
): Promise<CommitInfo[]> {
): Promise<DiffInfo> {
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'] =
@@ -50,6 +70,16 @@ export class Commits {
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}^`
}
@@ -58,16 +88,23 @@ export class Commits {
`️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`
)
return commits
.filter(commit => commit.sha)
.map(commit => ({
sha: commit.sha || '',
summary: commit.commit.message.split('\n')[0],
message: commit.commit.message,
date: moment(commit.commit.committer?.date),
author: commit.commit.author?.name || '',
prNumber: undefined
}))
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,
date: moment(commit.commit.committer?.date),
author: commit.commit.author?.name || '',
prNumber: undefined
}))
}
}
private sortCommits(commits: CommitInfo[]): CommitInfo[] {
+45 -22
View File
@@ -1,9 +1,9 @@
import * as core from '@actions/core'
import {CommitInfo, Commits, filterCommits} from './commits'
import {Commits, filterCommits, DiffInfo, DefaultDiffInfo} from './commits'
import {Configuration, DefaultConfiguration} from './configuration'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Octokit} from '@octokit/rest'
import {buildChangelog} from './transform'
import {buildChangelog, fillAdditionalPlaceholders} from './transform'
import {failOrError} from './utils'
export interface ReleaseNotesOptions {
@@ -21,11 +21,15 @@ export interface ReleaseNotesOptions {
export class ReleaseNotes {
constructor(private octokit: Octokit, private options: ReleaseNotesOptions) {}
async pull(): Promise<string | null> {
async pull(): Promise<string> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
if (!this.options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
mergedPullRequests = await this.getMergedPullRequests(this.octokit)
const [info, prs] = await this.getMergedPullRequests(this.octokit)
mergedPullRequests = prs
diffInfo = info
// define the included PRs within this release as output
core.setOutput(
@@ -37,57 +41,74 @@ export class ReleaseNotes {
.join(',')
)
core.setOutput('changed_files', diffInfo.changedFiles)
core.setOutput('additions', diffInfo.additions)
core.setOutput('deletions', diffInfo.deletions)
core.setOutput('changes', diffInfo.changes)
core.setOutput('commits', diffInfo.commits)
core.endGroup()
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
mergedPullRequests = await this.generateCommitPRs(this.octokit)
const [info, prs] = await this.generateCommitPRs(this.octokit)
mergedPullRequests = prs
diffInfo = info
core.endGroup()
}
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`)
return null
return fillAdditionalPlaceholders(
this.options.configuration.empty_template ||
DefaultConfiguration.empty_template,
this.options
)
}
core.startGroup('📦 Build changelog')
const resultChangelog = buildChangelog(mergedPullRequests, this.options)
const resultChangelog = buildChangelog(
diffInfo,
mergedPullRequests,
this.options
)
core.endGroup()
return resultChangelog
}
private async getCommitHistory(octokit: Octokit): Promise<CommitInfo[]> {
private async getCommitHistory(octokit: Octokit): Promise<DiffInfo> {
const {owner, repo, fromTag, toTag, failOnError} = this.options
core.info(`️ Comparing ${owner}/${repo} - '${fromTag}...${toTag}'`)
const commitsApi = new Commits(octokit)
let commits: CommitInfo[]
let diffInfo: DiffInfo
try {
commits = await commitsApi.getDiff(owner, repo, fromTag, toTag)
diffInfo = await commitsApi.getDiff(owner, repo, fromTag, toTag)
} catch (error) {
failOrError(
`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`,
failOnError
)
return []
return DefaultDiffInfo
}
if (commits.length === 0) {
if (diffInfo.commitInfo.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag}...${toTag}`)
return []
return DefaultDiffInfo
}
return commits
return diffInfo
}
private async getMergedPullRequests(
octokit: Octokit
): Promise<PullRequestInfo[]> {
): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, configuration} =
this.options
const commits = await this.getCommitHistory(octokit)
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return []
return [diffInfo, []]
}
const firstCommit = commits[0]
@@ -193,17 +214,18 @@ export class ReleaseNotes {
}
}
return finalPrs
return [diffInfo, finalPrs]
}
private async generateCommitPRs(
octokit: Octokit
): Promise<PullRequestInfo[]> {
): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, configuration} = this.options
const commits = await this.getCommitHistory(octokit)
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return []
return [diffInfo, []]
}
const prCommits = filterCommits(
@@ -214,7 +236,7 @@ export class ReleaseNotes {
core.info(`️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`)
return prCommits.map(function (commit): PullRequestInfo {
const prs = prCommits.map(function (commit): PullRequestInfo {
return {
number: 0,
title: commit.summary,
@@ -234,5 +256,6 @@ export class ReleaseNotes {
status: 'merged'
}
})
return [diffInfo, prs]
}
}
+1 -9
View File
@@ -4,7 +4,6 @@ import {Octokit} from '@octokit/rest'
import {ReleaseNotes} from './releaseNotes'
import {Tags} from './tags'
import {failOrError} from './utils'
import {fillAdditionalPlaceholders} from './transform'
export class ReleaseNotesBuilder {
constructor(
@@ -98,13 +97,6 @@ export class ReleaseNotesBuilder {
}
const releaseNotes = new ReleaseNotes(octokit, options)
return (
(await releaseNotes.pull()) ||
fillAdditionalPlaceholders(
this.configuration.empty_template ||
DefaultConfiguration.empty_template,
options
)
)
return await releaseNotes.pull()
}
}
+24
View File
@@ -7,8 +7,10 @@ import {
} from './configuration'
import {PullRequestInfo, sortPullRequests} from './pullRequests'
import {ReleaseNotesOptions} from './releaseNotes'
import {DiffInfo} from './commits'
export function buildChangelog(
diffInfo: DiffInfo,
prs: PullRequestInfo[],
options: ReleaseNotesOptions
): string {
@@ -272,6 +274,27 @@ export function buildChangelog(
/\${{IGNORED_COUNT}}/g,
ignoredPrs.length.toString()
)
// code change placeholders
transformedChangelog = transformedChangelog.replace(
/\${{CHANGED_FILES}}/g,
diffInfo.changedFiles.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{ADDITIONS}}/g,
diffInfo.additions.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{DELETIONS}}/g,
diffInfo.deletions.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{CHANGES}}/g,
diffInfo.changes.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{COMMITS}}/g,
diffInfo.commits.toString()
)
transformedChangelog = fillAdditionalPlaceholders(
transformedChangelog,
options
@@ -286,6 +309,7 @@ export function fillAdditionalPlaceholders(
options: ReleaseNotesOptions
): string {
let transformed = text
// repository placeholders
transformed = transformed.replace(/\${{OWNER}}/g, options.owner)
transformed = transformed.replace(/\${{REPO}}/g, options.repo)
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag)