- refactor action and move different functionalities in better classes

- merge releaseNotes and releaseNotesBuilder
- update testcases to cover new structure
This commit is contained in:
Mike Penz
2023-05-26 12:09:48 +00:00
committed by GitHub
parent 061b8ff198
commit 66470e5f14
8 changed files with 473 additions and 494 deletions
@@ -1,6 +1,7 @@
import {ReleaseNotes} from '../src/releaseNotes'
import {mergeConfiguration, resolveConfiguration} from '../src/utils'
import {pullData} from '../src/releaseNotesBuilder'
import {Octokit} from '@octokit/rest'
import { buildChangelog } from '../src/transform'
jest.setTimeout(180000)
@@ -11,7 +12,8 @@ const octokit = new Octokit({
it('Should have empty changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.0.1'},
@@ -25,14 +27,14 @@ it('Should have empty changelog (tags)', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual('- no changes')
})
it('Should match generated changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.0.1'},
@@ -46,7 +48,7 @@ it('Should match generated changelog (tags)', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🧪 Tests
@@ -58,7 +60,7 @@ it('Should match generated changelog (tags)', async () => {
it('Should match generated changelog (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_all_placeholders.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'},
@@ -72,7 +74,7 @@ it('Should match generated changelog (refs)', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🧪 Tests
@@ -92,7 +94,7 @@ nhoelzl
it('Should match generated changelog and replace all occurrences (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_replace_all_placeholders.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'},
@@ -106,7 +108,7 @@ it('Should match generated changelog and replace all occurrences (refs)', async
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🧪 Tests
@@ -128,7 +130,7 @@ nhoelzl
it('Should match ordered ASC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_asc.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.3.0'},
@@ -142,14 +144,14 @@ it('Should match ordered ASC', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features\n\n22\n24\n25\n26\n28\n\n## 🐛 Fixes\n\n23\n\n`)
})
it('Should match ordered DESC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_desc.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.3.0'},
@@ -163,14 +165,14 @@ it('Should match ordered DESC', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features\n\n28\n26\n25\n24\n22\n\n## 🐛 Fixes\n\n23\n\n`)
})
it('Should match ordered by title ASC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_asc.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.3.0'},
@@ -184,7 +186,7 @@ it('Should match ordered by title ASC', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features\n\nEnhanced action logs\nImprove README\nImproved configuration failure handling\nImproved defaults if no configuration is provided\nIntroduce additional placeholders [milestone, labels, assignees, reviewers]\n\n## 🐛 Fixes\n\nImproved handling for non existing tags\n\n`
@@ -193,7 +195,7 @@ it('Should match ordered by title ASC', async () => {
it('Should match ordered by title DESC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_desc.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v0.3.0'},
@@ -207,7 +209,7 @@ it('Should match ordered by title DESC', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features\n\nIntroduce additional placeholders [milestone, labels, assignees, reviewers]\nImproved defaults if no configuration is provided\nImproved configuration failure handling\nImprove README\nEnhanced action logs\n\n## 🐛 Fixes\n\nImproved handling for non existing tags\n\n`
@@ -216,7 +218,7 @@ it('Should match ordered by title DESC', async () => {
it('Should ignore PRs not merged into develop branch', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_develop.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v1.3.1'},
@@ -230,14 +232,14 @@ it('Should ignore PRs not merged into develop branch', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`150\n\n`)
})
it('Should ignore PRs not merged into main branch', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_main.json'))
const releaseNotes = new ReleaseNotes(octokit, {
const data = await pullData(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: {name: 'v1.3.1'},
@@ -251,7 +253,7 @@ it('Should ignore PRs not merged into main branch', async () => {
configuration
})
const changeLog = await releaseNotes.pull()
const changeLog = buildChangelog(data.diffInfo, data.mergedPullRequests, data.options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`153\n\n`)
})
Generated Vendored
+198 -235
View File
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.filterCommits = exports.Commits = exports.DefaultDiffInfo = void 0;
const core = __importStar(__nccwpck_require__(2186));
const moment_1 = __importDefault(__nccwpck_require__(9623));
const utils_1 = __nccwpck_require__(918);
exports.DefaultDiffInfo = {
changedFiles: 0,
additions: 0,
@@ -144,6 +145,59 @@ class Commits {
});
return commitsResult;
}
getCommitHistory(options) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, fromTag, toTag, failOnError } = options;
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`);
const commitsApi = new Commits(this.octokit);
let diffInfo;
try {
diffInfo = yield commitsApi.getDiff(owner, repo, fromTag.name, toTag.name);
}
catch (error) {
(0, utils_1.failOrError)(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError);
return exports.DefaultDiffInfo;
}
if (diffInfo.commitInfo.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`);
return exports.DefaultDiffInfo;
}
return diffInfo;
});
}
generateCommitPRs(options) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, configuration } = options;
const diffInfo = yield 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) {
return {
number: 0,
title: commit.summary,
htmlURL: '',
baseBranch: '',
createdAt: commit.date,
mergedAt: commit.date,
mergeCommitSha: commit.sha,
author: commit.author || '',
repoName: '',
labels: new Set(),
milestone: '',
body: commit.message || '',
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
};
});
return [diffInfo, prs];
});
}
}
exports.Commits = Commits;
/**
@@ -503,6 +557,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.retrieveProperty = exports.compare = exports.sortPullRequests = exports.PullRequests = exports.EMPTY_COMMENT_INFO = void 0;
const core = __importStar(__nccwpck_require__(2186));
const moment_1 = __importDefault(__nccwpck_require__(9623));
const commits_1 = __nccwpck_require__(3916);
exports.EMPTY_COMMENT_INFO = {
id: 0,
htmlURL: '',
@@ -512,8 +567,9 @@ exports.EMPTY_COMMENT_INFO = {
state: undefined
};
class PullRequests {
constructor(octokit) {
constructor(octokit, commits) {
this.octokit = octokit;
this.commits = commits;
}
getSingle(owner, repo, prNumber) {
return __awaiter(this, void 0, void 0, function* () {
@@ -662,6 +718,87 @@ class PullRequests {
pr.reviews = prReviews;
});
}
getMergedPullRequests(options) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration } = options;
const diffInfo = yield this.commits.getCommitHistory(options);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
return [diffInfo, []];
}
const firstCommit = commits[0];
const lastCommit = commits[commits.length - 1];
let fromDate = firstCommit.date;
const toDate = lastCommit.date;
const maxDays = configuration.max_back_track_time_days;
const maxFromDate = toDate.clone().subtract(maxDays, 'days');
if (maxFromDate.isAfter(fromDate)) {
core.info(`⚠️ Adjusted 'fromDate' to go max ${maxDays} back`);
fromDate = maxFromDate;
}
core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`);
const pullRequests = yield this.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests);
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`);
const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches);
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`);
// create array of commits for this release
const releaseCommitHashes = prCommits.map(commmit => {
return commmit.sha;
});
// filter out pull requests not associated with this release
const mergedPullRequests = pullRequests.filter(pr => {
return releaseCommitHashes.includes(pr.mergeCommitSha);
});
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`);
let allPullRequests = mergedPullRequests;
if (includeOpen) {
// retrieve all open pull requests
const openPullRequests = yield this.getOpen(owner, repo, configuration.max_pull_requests);
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`);
// all pull requests
allPullRequests = allPullRequests.concat(openPullRequests);
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`);
}
// retrieve base branches we allow
const baseBranches = configuration.base_branches;
const baseBranchPatterns = baseBranches.map(baseBranch => {
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu');
});
// return only prs if the baseBranch is matching the configuration
const finalPrs = allPullRequests.filter(pr => {
if (baseBranches.length !== 0) {
return baseBranchPatterns.some(pattern => {
return pr.baseBranch.match(pattern) !== null;
});
}
return true;
});
if (baseBranches.length !== 0) {
core.info(`️ Retrieved ${finalPrs.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`);
}
// fetch reviewers only if enabled (requires an additional API request per PR)
if (fetchReviews || fetchReviewers) {
core.info(`️ Fetching reviews (or reviewers) was enabled`);
// update PR information with reviewers who approved
for (const pr of finalPrs) {
yield this.getReviews(owner, repo, pr);
const reviews = pr.reviews;
if (reviews && ((reviews === null || reviews === void 0 ? void 0 : reviews.length) || 0) > 0) {
core.info(`️ Retrieved ${reviews.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`);
// backwards compatiblity
pr.approvedReviewers = reviews.filter(r => r.state === 'APPROVED').map(r => r.author);
}
else {
core.debug(`No reviewer(s) for PR ${owner}/${repo}/#${pr.number}`);
}
}
}
else {
core.debug(`️ Fetching reviews (or reviewers) was disabled`);
}
return [diffInfo, finalPrs];
});
}
}
exports.PullRequests = PullRequests;
function sortPrs(pullRequests) {
@@ -900,236 +1037,6 @@ function buildRegex(regex, target, onProperty, method, onEmpty) {
exports.buildRegex = buildRegex;
/***/ }),
/***/ 5882:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ReleaseNotes = void 0;
const core = __importStar(__nccwpck_require__(2186));
const commits_1 = __nccwpck_require__(3916);
const pullRequests_1 = __nccwpck_require__(4217);
const transform_1 = __nccwpck_require__(1644);
const utils_1 = __nccwpck_require__(918);
class ReleaseNotes {
constructor(octokit, options) {
this.octokit = octokit;
this.options = options;
}
pull() {
return __awaiter(this, void 0, void 0, function* () {
let mergedPullRequests;
let diffInfo;
if (!this.options.commitMode) {
core.startGroup(`🚀 Load pull requests`);
const [info, prs] = yield this.getMergedPullRequests(this.octokit);
mergedPullRequests = prs;
diffInfo = info;
// define the included PRs within this release as output
core.setOutput('pull_requests', mergedPullRequests
.map(pr => {
return pr.number;
})
.join(','));
core.endGroup();
}
else {
core.startGroup(`🚀 Load commit history`);
core.info(`⚠️ Executing experimental commit mode`);
const [info, prs] = yield this.generateCommitPRs(this.octokit);
mergedPullRequests = prs;
diffInfo = info;
core.endGroup();
}
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);
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`);
return (0, transform_1.replaceEmptyTemplate)(this.options.configuration.empty_template, this.options);
}
core.startGroup('📦 Build changelog');
const resultChangelog = (0, transform_1.buildChangelog)(diffInfo, mergedPullRequests, this.options);
core.endGroup();
return resultChangelog;
});
}
getCommitHistory(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, fromTag, toTag, failOnError } = this.options;
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`);
const commitsApi = new commits_1.Commits(octokit);
let diffInfo;
try {
diffInfo = yield commitsApi.getDiff(owner, repo, fromTag.name, toTag.name);
}
catch (error) {
(0, utils_1.failOrError)(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError);
return commits_1.DefaultDiffInfo;
}
if (diffInfo.commitInfo.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`);
return commits_1.DefaultDiffInfo;
}
return diffInfo;
});
}
getMergedPullRequests(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration } = this.options;
const diffInfo = yield this.getCommitHistory(octokit);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
return [diffInfo, []];
}
const firstCommit = commits[0];
const lastCommit = commits[commits.length - 1];
let fromDate = firstCommit.date;
const toDate = lastCommit.date;
const maxDays = configuration.max_back_track_time_days;
const maxFromDate = toDate.clone().subtract(maxDays, 'days');
if (maxFromDate.isAfter(fromDate)) {
core.info(`⚠️ Adjusted 'fromDate' to go max ${maxDays} back`);
fromDate = maxFromDate;
}
core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`);
const pullRequestsApi = new pullRequests_1.PullRequests(octokit);
const pullRequests = yield pullRequestsApi.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests);
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`);
const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches);
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`);
// create array of commits for this release
const releaseCommitHashes = prCommits.map(commmit => {
return commmit.sha;
});
// filter out pull requests not associated with this release
const mergedPullRequests = pullRequests.filter(pr => {
return releaseCommitHashes.includes(pr.mergeCommitSha);
});
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`);
let allPullRequests = mergedPullRequests;
if (includeOpen) {
// retrieve all open pull requests
const openPullRequests = yield pullRequestsApi.getOpen(owner, repo, configuration.max_pull_requests);
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`);
// all pull requests
allPullRequests = allPullRequests.concat(openPullRequests);
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`);
}
// retrieve base branches we allow
const baseBranches = configuration.base_branches;
const baseBranchPatterns = baseBranches.map(baseBranch => {
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu');
});
// return only prs if the baseBranch is matching the configuration
const finalPrs = allPullRequests.filter(pr => {
if (baseBranches.length !== 0) {
return baseBranchPatterns.some(pattern => {
return pr.baseBranch.match(pattern) !== null;
});
}
return true;
});
if (baseBranches.length !== 0) {
core.info(`️ Retrieved ${finalPrs.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`);
}
// fetch reviewers only if enabled (requires an additional API request per PR)
if (fetchReviews || fetchReviewers) {
core.info(`️ Fetching reviews (or reviewers) was enabled`);
// update PR information with reviewers who approved
for (const pr of finalPrs) {
yield pullRequestsApi.getReviews(owner, repo, pr);
const reviews = pr.reviews;
if (reviews && ((reviews === null || reviews === void 0 ? void 0 : reviews.length) || 0) > 0) {
core.info(`️ Retrieved ${reviews.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`);
// backwards compatiblity
pr.approvedReviewers = reviews.filter(r => r.state === 'APPROVED').map(r => r.author);
}
else {
core.debug(`No reviewer(s) for PR ${owner}/${repo}/#${pr.number}`);
}
}
}
else {
core.debug(`️ Fetching reviews (or reviewers) was disabled`);
}
return [diffInfo, finalPrs];
});
}
generateCommitPRs(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, configuration } = this.options;
const diffInfo = yield this.getCommitHistory(octokit);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
return [diffInfo, []];
}
const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches);
core.info(`️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`);
const prs = prCommits.map(function (commit) {
return {
number: 0,
title: commit.summary,
htmlURL: '',
baseBranch: '',
createdAt: commit.date,
mergedAt: commit.date,
mergeCommitSha: commit.sha,
author: commit.author || '',
repoName: '',
labels: new Set(),
milestone: '',
body: commit.message || '',
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
};
});
return [diffInfo, prs];
});
}
}
exports.ReleaseNotes = ReleaseNotes;
/***/ }),
/***/ 4883:
@@ -1170,13 +1077,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ReleaseNotesBuilder = void 0;
exports.pullData = exports.ReleaseNotesBuilder = void 0;
const core = __importStar(__nccwpck_require__(2186));
const rest_1 = __nccwpck_require__(5375);
const releaseNotes_1 = __nccwpck_require__(5882);
const tags_1 = __nccwpck_require__(7532);
const utils_1 = __nccwpck_require__(918);
const https_proxy_agent_1 = __nccwpck_require__(7219);
const pullRequests_1 = __nccwpck_require__(4217);
const commits_1 = __nccwpck_require__(3916);
const transform_1 = __nccwpck_require__(1644);
class ReleaseNotesBuilder {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, fetchReleaseInformation = false, fetchReviews = false, commitMode, configuration) {
this.baseUrl = baseUrl;
@@ -1278,12 +1187,58 @@ class ReleaseNotesBuilder {
commitMode: this.commitMode,
configuration: this.configuration
};
const releaseNotes = new releaseNotes_1.ReleaseNotes(octokit, options);
return yield releaseNotes.pull();
const releaseNotesData = yield pullData(octokit, options);
return (0, transform_1.buildChangelog)(releaseNotesData.diffInfo, releaseNotesData.mergedPullRequests, releaseNotesData.options);
});
}
}
exports.ReleaseNotesBuilder = ReleaseNotesBuilder;
function pullData(octokit, options) {
return __awaiter(this, void 0, void 0, function* () {
let mergedPullRequests;
let diffInfo;
const commitsApi = new commits_1.Commits(octokit);
if (!options.commitMode) {
core.startGroup(`🚀 Load pull requests`);
const pullRequestsApi = new pullRequests_1.PullRequests(octokit, commitsApi);
const [info, prs] = yield pullRequestsApi.getMergedPullRequests(options);
mergedPullRequests = prs;
diffInfo = info;
}
else {
core.startGroup(`🚀 Load commit history`);
core.info(`⚠️ Executing experimental commit mode`);
const [info, prs] = yield commitsApi.generateCommitPRs(options);
mergedPullRequests = prs;
diffInfo = info;
}
// define the included PRs within this release as output
core.setOutput('pull_requests', mergedPullRequests
.map(pr => {
return pr.number;
})
.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);
const collectAndExport = true;
if (collectAndExport) {
core.info('📦 Exporting collected data');
core.exportVariable('_diffInfo', JSON.stringify(diffInfo));
core.exportVariable('_mergedPullRequests', JSON.stringify(mergedPullRequests));
core.exportVariable('_options', JSON.stringify(options));
}
core.endGroup();
return {
diffInfo,
mergedPullRequests,
options
};
});
}
exports.pullData = pullData;
/***/ }),
@@ -1687,6 +1642,13 @@ const utils_1 = __nccwpck_require__(918);
const regexUtils_1 = __nccwpck_require__(2364);
const EMPTY_MAP = new Map();
function buildChangelog(diffInfo, prs, options) {
core.startGroup('📦 Build changelog');
if (prs.length === 0) {
core.warning(`⚠️ No pull requests found`);
const result = replaceEmptyTemplate(options.configuration.empty_template, options);
core.endGroup();
return result;
}
// sort to target order
const config = options.configuration;
const sort = config.sort;
@@ -1916,6 +1878,7 @@ function buildChangelog(diffInfo, prs, options) {
transformedChangelog = replacePrPlaceholders(transformedChangelog, placeholderPrMap, config);
transformedChangelog = cleanupPrPlaceholders(transformedChangelog, placeholders);
core.info(`️ Filled template`);
core.endGroup();
return transformedChangelog;
}
exports.buildChangelog = buildChangelog;
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+59
View File
@@ -1,6 +1,9 @@
import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import moment from 'moment'
import {failOrError} from './utils'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
import {PullRequestInfo} from './pullRequests'
export interface DiffInfo {
changedFiles: number
@@ -117,6 +120,62 @@ export class Commits {
return commitsResult
}
async getCommitHistory(options: ReleaseNotesOptions): Promise<DiffInfo> {
const {owner, repo, fromTag, toTag, failOnError} = options
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`)
const commitsApi = new Commits(this.octokit)
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: ReleaseNotesOptions): 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.date,
mergedAt: commit.date,
mergeCommitSha: commit.sha,
author: commit.author || '',
repoName: '',
labels: new Set(),
milestone: '',
body: commit.message || '',
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
}
})
return [diffInfo, prs]
}
}
/**
+103 -1
View File
@@ -3,6 +3,8 @@ import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import {Unpacked} from './utils'
import moment from 'moment'
import {Property, Sort} from './configuration'
import {Commits, DiffInfo, filterCommits} from './commits'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
export interface PullRequestInfo {
number: number
@@ -50,7 +52,7 @@ type PullsListData = RestEndpointMethodTypes['pulls']['list']['response']['data'
type PullReviewsData = RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
export class PullRequests {
constructor(private octokit: Octokit) {}
constructor(private octokit: Octokit, private commits: Commits) {}
async getSingle(owner: string, repo: string, prNumber: number): Promise<PullRequestInfo | null> {
try {
@@ -159,6 +161,106 @@ export class PullRequests {
}
pr.reviews = prReviews
}
async getMergedPullRequests(options: ReleaseNotesOptions): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration} = options
const diffInfo = await this.commits.getCommitHistory(options)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return [diffInfo, []]
}
const firstCommit = commits[0]
const lastCommit = commits[commits.length - 1]
let fromDate = firstCommit.date
const toDate = lastCommit.date
const maxDays = configuration.max_back_track_time_days
const maxFromDate = toDate.clone().subtract(maxDays, 'days')
if (maxFromDate.isAfter(fromDate)) {
core.info(`⚠️ Adjusted 'fromDate' to go max ${maxDays} back`)
fromDate = maxFromDate
}
core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`)
const pullRequests = await this.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests)
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`)
const prCommits = filterCommits(commits, configuration.exclude_merge_branches)
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`)
// create array of commits for this release
const releaseCommitHashes = prCommits.map(commmit => {
return commmit.sha
})
// filter out pull requests not associated with this release
const mergedPullRequests = pullRequests.filter(pr => {
return releaseCommitHashes.includes(pr.mergeCommitSha)
})
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`)
let allPullRequests = mergedPullRequests
if (includeOpen) {
// retrieve all open pull requests
const openPullRequests = await this.getOpen(owner, repo, configuration.max_pull_requests)
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`)
// all pull requests
allPullRequests = allPullRequests.concat(openPullRequests)
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`)
}
// retrieve base branches we allow
const baseBranches = configuration.base_branches
const baseBranchPatterns = baseBranches.map(baseBranch => {
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu')
})
// return only prs if the baseBranch is matching the configuration
const finalPrs = allPullRequests.filter(pr => {
if (baseBranches.length !== 0) {
return baseBranchPatterns.some(pattern => {
return pr.baseBranch.match(pattern) !== null
})
}
return true
})
if (baseBranches.length !== 0) {
core.info(`️ Retrieved ${finalPrs.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`)
}
// fetch reviewers only if enabled (requires an additional API request per PR)
if (fetchReviews || fetchReviewers) {
core.info(`️ Fetching reviews (or reviewers) was enabled`)
// update PR information with reviewers who approved
for (const pr of finalPrs) {
await this.getReviews(owner, repo, pr)
const reviews = pr.reviews
if (reviews && (reviews?.length || 0) > 0) {
core.info(`️ Retrieved ${reviews.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`)
// backwards compatiblity
pr.approvedReviewers = reviews.filter(r => r.state === 'APPROVED').map(r => r.author)
} else {
core.debug(`No reviewer(s) for PR ${owner}/${repo}/#${pr.number}`)
}
}
} else {
core.debug(`️ Fetching reviews (or reviewers) was disabled`)
}
return [diffInfo, finalPrs]
}
}
function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] {
-230
View File
@@ -1,230 +0,0 @@
import * as core from '@actions/core'
import {Commits, filterCommits, DiffInfo, DefaultDiffInfo} from './commits'
import {Configuration} from './configuration'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Octokit} from '@octokit/rest'
import {buildChangelog, replaceEmptyTemplate} from './transform'
import {failOrError} from './utils'
import {TagInfo} from './tags'
export interface ReleaseNotesOptions {
owner: string // the owner of the repository
repo: string // the repository
fromTag: TagInfo // the tag/ref to start from
toTag: TagInfo // the tag/ref up to
includeOpen: boolean // defines if we should also fetch open pull requests
failOnError: boolean // defines if we should fail the action in case of an error
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
fetchReleaseInformation: boolean // defines if the action should fetch the release information for the from and to tag - e.g. the creation date for the associated release
fetchReviews: boolean // defines if the action should fetch the reviews for the PR.
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported
configuration: Configuration // the configuration as defined in `configuration.ts`
}
export class ReleaseNotes {
constructor(private octokit: Octokit, private options: ReleaseNotesOptions) {}
async pull(): Promise<string> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
if (!this.options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
const [info, prs] = await this.getMergedPullRequests(this.octokit)
mergedPullRequests = prs
diffInfo = info
// define the included PRs within this release as output
core.setOutput(
'pull_requests',
mergedPullRequests
.map(pr => {
return pr.number
})
.join(',')
)
core.endGroup()
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
const [info, prs] = await this.generateCommitPRs(this.octokit)
mergedPullRequests = prs
diffInfo = info
core.endGroup()
}
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)
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`)
return replaceEmptyTemplate(this.options.configuration.empty_template, this.options)
}
core.startGroup('📦 Build changelog')
const resultChangelog = buildChangelog(diffInfo, mergedPullRequests, this.options)
core.endGroup()
return resultChangelog
}
private async getCommitHistory(octokit: Octokit): Promise<DiffInfo> {
const {owner, repo, fromTag, toTag, failOnError} = this.options
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`)
const commitsApi = new Commits(octokit)
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
}
private async getMergedPullRequests(octokit: Octokit): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration} = this.options
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return [diffInfo, []]
}
const firstCommit = commits[0]
const lastCommit = commits[commits.length - 1]
let fromDate = firstCommit.date
const toDate = lastCommit.date
const maxDays = configuration.max_back_track_time_days
const maxFromDate = toDate.clone().subtract(maxDays, 'days')
if (maxFromDate.isAfter(fromDate)) {
core.info(`⚠️ Adjusted 'fromDate' to go max ${maxDays} back`)
fromDate = maxFromDate
}
core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`)
const pullRequestsApi = new PullRequests(octokit)
const pullRequests = await pullRequestsApi.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests)
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`)
const prCommits = filterCommits(commits, configuration.exclude_merge_branches)
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`)
// create array of commits for this release
const releaseCommitHashes = prCommits.map(commmit => {
return commmit.sha
})
// filter out pull requests not associated with this release
const mergedPullRequests = pullRequests.filter(pr => {
return releaseCommitHashes.includes(pr.mergeCommitSha)
})
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`)
let allPullRequests = mergedPullRequests
if (includeOpen) {
// retrieve all open pull requests
const openPullRequests = await pullRequestsApi.getOpen(owner, repo, configuration.max_pull_requests)
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`)
// all pull requests
allPullRequests = allPullRequests.concat(openPullRequests)
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`)
}
// retrieve base branches we allow
const baseBranches = configuration.base_branches
const baseBranchPatterns = baseBranches.map(baseBranch => {
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu')
})
// return only prs if the baseBranch is matching the configuration
const finalPrs = allPullRequests.filter(pr => {
if (baseBranches.length !== 0) {
return baseBranchPatterns.some(pattern => {
return pr.baseBranch.match(pattern) !== null
})
}
return true
})
if (baseBranches.length !== 0) {
core.info(`️ Retrieved ${finalPrs.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`)
}
// fetch reviewers only if enabled (requires an additional API request per PR)
if (fetchReviews || fetchReviewers) {
core.info(`️ Fetching reviews (or reviewers) was enabled`)
// update PR information with reviewers who approved
for (const pr of finalPrs) {
await pullRequestsApi.getReviews(owner, repo, pr)
const reviews = pr.reviews
if (reviews && (reviews?.length || 0) > 0) {
core.info(`️ Retrieved ${reviews.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`)
// backwards compatiblity
pr.approvedReviewers = reviews.filter(r => r.state === 'APPROVED').map(r => r.author)
} else {
core.debug(`No reviewer(s) for PR ${owner}/${repo}/#${pr.number}`)
}
}
} else {
core.debug(`️ Fetching reviews (or reviewers) was disabled`)
}
return [diffInfo, finalPrs]
}
private async generateCommitPRs(octokit: Octokit): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, configuration} = this.options
const diffInfo = await this.getCommitHistory(octokit)
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.date,
mergedAt: commit.date,
mergeCommitSha: commit.sha,
author: commit.author || '',
repoName: '',
labels: new Set(),
milestone: '',
body: commit.message || '',
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
}
})
return [diffInfo, prs]
}
}
+77 -4
View File
@@ -1,10 +1,32 @@
import * as core from '@actions/core'
import {Configuration} from './configuration'
import {Octokit} from '@octokit/rest'
import {ReleaseNotes} from './releaseNotes'
import {Tags} from './tags'
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 {buildChangelog} from './transform'
export interface ReleaseNotesOptions {
owner: string // the owner of the repository
repo: string // the repository
fromTag: TagInfo // the tag/ref to start from
toTag: TagInfo // the tag/ref up to
includeOpen: boolean // defines if we should also fetch open pull requests
failOnError: boolean // defines if we should fail the action in case of an error
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
fetchReleaseInformation: boolean // defines if the action should fetch the release information for the from and to tag - e.g. the creation date for the associated release
fetchReviews: boolean // defines if the action should fetch the reviews for the PR.
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported
configuration: Configuration // the configuration as defined in `configuration.ts`
}
export interface ReleaseNotesData {
diffInfo: DiffInfo
mergedPullRequests: PullRequestInfo[]
options: ReleaseNotesOptions
}
export class ReleaseNotesBuilder {
constructor(
@@ -122,8 +144,59 @@ export class ReleaseNotesBuilder {
commitMode: this.commitMode,
configuration: this.configuration
}
const releaseNotes = new ReleaseNotes(octokit, options)
return await releaseNotes.pull()
const releaseNotesData = await pullData(octokit, options)
return buildChangelog(releaseNotesData.diffInfo, releaseNotesData.mergedPullRequests, releaseNotesData.options)
}
}
export async function pullData(octokit: Octokit, options: ReleaseNotesOptions): Promise<ReleaseNotesData> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
const commitsApi = new Commits(octokit)
if (!options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
const pullRequestsApi = new PullRequests(octokit, commitsApi)
const [info, prs] = await pullRequestsApi.getMergedPullRequests(options)
mergedPullRequests = prs
diffInfo = info
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
const [info, prs] = await commitsApi.generateCommitPRs(options)
mergedPullRequests = prs
diffInfo = info
}
// define the included PRs within this release as output
core.setOutput(
'pull_requests',
mergedPullRequests
.map(pr => {
return pr.number
})
.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)
const collectAndExport = true
if (collectAndExport) {
core.info('📦 Exporting collected data')
core.exportVariable('_diffInfo', JSON.stringify(diffInfo))
core.exportVariable('_mergedPullRequests', JSON.stringify(mergedPullRequests))
core.exportVariable('_options', JSON.stringify(options))
}
core.endGroup()
return {
diffInfo,
mergedPullRequests,
options
}
}
+12 -2
View File
@@ -1,7 +1,7 @@
import * as core from '@actions/core'
import {Category, Configuration, Placeholder, Property, Transformer} from './configuration'
import {CommentInfo, EMPTY_COMMENT_INFO, PullRequestInfo, retrieveProperty, sortPullRequests} from './pullRequests'
import {ReleaseNotesOptions} from './releaseNotes'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
import {DiffInfo} from './commits'
import {createOrSet, haveCommonElements, haveEveryElements} from './utils'
import {matchesRules, RegexTransformer, validateTransformer} from './regexUtils'
@@ -9,6 +9,14 @@ import {matchesRules, RegexTransformer, validateTransformer} from './regexUtils'
const EMPTY_MAP = new Map<string, string>()
export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], options: ReleaseNotesOptions): string {
core.startGroup('📦 Build changelog')
if (prs.length === 0) {
core.warning(`⚠️ No pull requests found`)
const result = replaceEmptyTemplate(options.configuration.empty_template, options)
core.endGroup()
return result
}
// sort to target order
const config = options.configuration
const sort = config.sort
@@ -181,7 +189,8 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
// serialize and provide the categorized content as json
const transformedCategorized = Array.from(categorized).reduce(
(obj, [key, value]) => Object.assign(obj, {[key.key || key.title]: value}), {}
(obj, [key, value]) => Object.assign(obj, {[key.key || key.title]: value}),
{}
)
core.setOutput('categorized', JSON.stringify(transformedCategorized))
@@ -273,6 +282,7 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
transformedChangelog = replacePrPlaceholders(transformedChangelog, placeholderPrMap, config)
transformedChangelog = cleanupPrPlaceholders(transformedChangelog, placeholders)
core.info(`️ Filled template`)
core.endGroup()
return transformedChangelog
}