Merge pull request #1373 from selfdocumentingcode/refactor/transform-and-misc
Refactor transform.ts and others
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
name: 'CI'
|
name: "CI"
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -25,13 +25,13 @@ jobs:
|
|||||||
- name: Install NPM
|
- name: Install NPM
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
- name: Run NPM
|
- name: Run NPM
|
||||||
run: |
|
run: |
|
||||||
npm run all
|
npm run all
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Echo Configuration without Checkout Changelog
|
- name: Echo Configuration without Checkout Changelog
|
||||||
env:
|
env:
|
||||||
CHANGELOG: ${{ steps.without_checkout.outputs.changelog }}
|
CHANGELOG: ${{ steps.without_checkout.outputs.changelog }}
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Echo Minimal Configuration Changelog
|
- name: Echo Minimal Configuration Changelog
|
||||||
env:
|
env:
|
||||||
CHANGELOG: ${{ steps.minimal_release.outputs.changelog }}
|
CHANGELOG: ${{ steps.minimal_release.outputs.changelog }}
|
||||||
@@ -125,7 +125,7 @@ jobs:
|
|||||||
token: ${{ secrets.PERSONAL_TOKEN }}
|
token: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
exportCache: true
|
exportCache: true
|
||||||
exportOnly: true
|
exportOnly: true
|
||||||
|
|
||||||
- name: "External Repo Configuration"
|
- name: "External Repo Configuration"
|
||||||
id: external_changelog
|
id: external_changelog
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -188,4 +188,3 @@ jobs:
|
|||||||
prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-b') || contains(github.ref, '-a') }}
|
prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-b') || contains(github.ref, '-a') }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -6,5 +6,13 @@
|
|||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"bracketSpacing": false,
|
"bracketSpacing": false,
|
||||||
"arrowParens": "avoid"
|
"arrowParens": "avoid",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.yml", "*.yaml"],
|
||||||
|
"options": {
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ test('missing values should result in failure', () => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cp.execSync(`node ${ip}`, options).toString()
|
cp.execSync(`node ${ip}`, options).toString()
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
expect(true).toBe(true)
|
expect(true).toBe(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ it('[Github] Uncategorized category', async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('[Github] Verify commit based changelog', async () => {
|
it('[Github] Verify commit based changelog', async () => {
|
||||||
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits.json'))
|
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits.json'), 'COMMIT')
|
||||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||||
null,
|
null,
|
||||||
githubRepository,
|
githubRepository,
|
||||||
@@ -257,7 +257,7 @@ it('[Github] Verify commit based changelog', async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('[Github] Verify commit based changelog, with emoji categorisation', async () => {
|
it('[Github] Verify commit based changelog, with emoji categorisation', async () => {
|
||||||
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits_emoji.json'))
|
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits_emoji.json'), 'COMMIT')
|
||||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||||
null,
|
null,
|
||||||
githubRepository,
|
githubRepository,
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ it('Extract label from title, split regex', async () => {
|
|||||||
on_property: 'title'
|
on_property: 'title'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
expect(buildChangelogTest(configuration, mergedPullRequests, repositoryUtils)).toStrictEqual(
|
expect(buildChangelogTest(configuration, mergedPullRequests, repositoryUtils)).toStrictEqual(
|
||||||
`## 🚀 Features\n\n- [Feature][AB-1234] - this is a PR 1 title message\n - PR: #1\n- [Issue][Feature][AB-1234321] - this is a PR 3 title message\n - PR: #3\n\n## 🐛 Fixes\n\n- [Issue][AB-4321] - this is a PR 2 title message\n - PR: #2\n- [Issue][Feature][AB-1234321] - this is a PR 3 title message\n - PR: #3\n\n`
|
`## 🚀 Features\n\n- [Feature][AB-1234] - this is a PR 1 title message\n - PR: #1\n- [Issue][Feature][AB-1234321] - this is a PR 3 title message\n - PR: #3\n\n## 🐛 Fixes\n\n- [Issue][AB-4321] - this is a PR 2 title message\n - PR: #2\n- [Issue][Feature][AB-1234321] - this is a PR 3 title message\n - PR: #3\n\n`
|
||||||
)
|
)
|
||||||
@@ -448,7 +449,8 @@ it('Use empty_content for empty category', async () => {
|
|||||||
labels: ['Feature']
|
labels: ['Feature']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
expect(buildChangelogTest(customConfig, pullRequestsWithLabels, repositoryUtils)).toStrictEqual(
|
const changelog = buildChangelogTest(customConfig, pullRequestsWithLabels, repositoryUtils)
|
||||||
|
expect(changelog).toStrictEqual(
|
||||||
`## 🚀 Features and 🐛 Issues\n\n- No PRs in this category\n\n## 🚀 Features\n\n- [ABC-1234] - this is a PR 1 title message\n - PR: #1\n- [ABC-1234] - this is a PR 3 title message\n - PR: #3\n\n`
|
`## 🚀 Features and 🐛 Issues\n\n- No PRs in this category\n\n## 🚀 Features\n\n- [ABC-1234] - this is a PR 1 title message\n - PR: #1\n- [ABC-1234] - this is a PR 3 title message\n - PR: #3\n\n`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+321
-224
@@ -7,7 +7,10 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.DefaultCommitConfiguration = exports.DefaultConfiguration = void 0;
|
exports.DefaultCommitConfiguration = exports.DefaultConfiguration = exports.PlaceholderGroup = void 0;
|
||||||
|
class PlaceholderGroup extends Map {
|
||||||
|
}
|
||||||
|
exports.PlaceholderGroup = PlaceholderGroup;
|
||||||
exports.DefaultConfiguration = {
|
exports.DefaultConfiguration = {
|
||||||
max_tags_to_fetch: 200, // the amount of tags to fetch from the github API
|
max_tags_to_fetch: 200, // the amount of tags to fetch from the github API
|
||||||
max_pull_requests: 200, // the amount of pull requests to process
|
max_pull_requests: 200, // the amount of pull requests to process
|
||||||
@@ -53,16 +56,7 @@ exports.DefaultConfiguration = {
|
|||||||
custom_placeholders: [],
|
custom_placeholders: [],
|
||||||
trim_values: false // defines if values are being trimmed prior to inserting
|
trim_values: false // defines if values are being trimmed prior to inserting
|
||||||
};
|
};
|
||||||
exports.DefaultCommitConfiguration = {
|
exports.DefaultCommitConfiguration = Object.assign(Object.assign({}, exports.DefaultConfiguration), { pr_template: '- #{{TITLE}}', categories: [
|
||||||
max_tags_to_fetch: exports.DefaultConfiguration.max_tags_to_fetch,
|
|
||||||
max_pull_requests: exports.DefaultConfiguration.max_pull_requests,
|
|
||||||
max_back_track_time_days: exports.DefaultConfiguration.max_back_track_time_days,
|
|
||||||
exclude_merge_branches: exports.DefaultConfiguration.exclude_merge_branches,
|
|
||||||
sort: exports.DefaultConfiguration.sort,
|
|
||||||
template: '#{{CHANGELOG}}', // the global template to host the changelog
|
|
||||||
pr_template: '- #{{TITLE}}', // the per PR template to pick for commit based mode
|
|
||||||
empty_template: exports.DefaultConfiguration.empty_template,
|
|
||||||
categories: [
|
|
||||||
{
|
{
|
||||||
title: '## 🚀 Features',
|
title: '## 🚀 Features',
|
||||||
labels: ['feature', 'feat']
|
labels: ['feature', 'feat']
|
||||||
@@ -79,20 +73,12 @@ exports.DefaultCommitConfiguration = {
|
|||||||
title: '## 📦 Other',
|
title: '## 📦 Other',
|
||||||
labels: []
|
labels: []
|
||||||
}
|
}
|
||||||
], // the categories to support for the ordering
|
], label_extractor: [
|
||||||
ignore_labels: exports.DefaultConfiguration.ignore_labels,
|
|
||||||
label_extractor: [
|
|
||||||
{
|
{
|
||||||
pattern: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\\([\\w\\-\\.]+\\))?(!)?: ([\\w ])+([\\s\\S]*)',
|
pattern: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\\([\\w\\-\\.]+\\))?(!)?: ([\\w ])+([\\s\\S]*)',
|
||||||
target: '$1'
|
target: '$1'
|
||||||
}
|
}
|
||||||
],
|
] });
|
||||||
transformers: exports.DefaultConfiguration.transformers,
|
|
||||||
tag_resolver: exports.DefaultConfiguration.tag_resolver,
|
|
||||||
base_branches: exports.DefaultConfiguration.base_branches,
|
|
||||||
custom_placeholders: exports.DefaultConfiguration.custom_placeholders,
|
|
||||||
trim_values: exports.DefaultConfiguration.trim_values
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -751,17 +737,17 @@ class PullRequests {
|
|||||||
}
|
}
|
||||||
getForCommitHash(owner, repo, commit_sha, maxPullRequests) {
|
getForCommitHash(owner, repo, commit_sha, maxPullRequests) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
return sortPrs(yield this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests));
|
return sortPrsByMergedAt(yield this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests) {
|
getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
return sortPrs(yield this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests));
|
return sortPrsByMergedAt(yield this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getOpen(owner, repo, maxPullRequests) {
|
getOpen(owner, repo, maxPullRequests) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
return sortPrs(yield this.repositoryUtils.getOpen(owner, repo, maxPullRequests));
|
return sortPrsByMergedAt(yield this.repositoryUtils.getOpen(owner, repo, maxPullRequests));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getReviews(owner, repo, pr) {
|
getReviews(owner, repo, pr) {
|
||||||
@@ -876,7 +862,7 @@ class PullRequests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.PullRequests = PullRequests;
|
exports.PullRequests = PullRequests;
|
||||||
function sortPrs(pullRequests) {
|
function sortPrsByMergedAt(pullRequests) {
|
||||||
return sortPullRequests(pullRequests, {
|
return sortPullRequests(pullRequests, {
|
||||||
order: 'ASC',
|
order: 'ASC',
|
||||||
on_property: 'mergedAt'
|
on_property: 'mergedAt'
|
||||||
@@ -1419,6 +1405,23 @@ function stringTags(tags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 3616:
|
||||||
|
/***/ ((__unused_webpack_module, exports) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.TemplateContext = exports.GroupedTemplateContext = void 0;
|
||||||
|
class GroupedTemplateContext extends Map {
|
||||||
|
}
|
||||||
|
exports.GroupedTemplateContext = GroupedTemplateContext;
|
||||||
|
class TemplateContext extends Map {
|
||||||
|
}
|
||||||
|
exports.TemplateContext = TemplateContext;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 8908:
|
/***/ 8908:
|
||||||
@@ -2559,13 +2562,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.clear = clear;
|
exports.clear = clear;
|
||||||
exports.buildChangelog = buildChangelog;
|
exports.buildChangelog = buildChangelog;
|
||||||
exports.replaceEmptyTemplate = replaceEmptyTemplate;
|
exports.renderEmptyChangelogTemplate = renderEmptyChangelogTemplate;
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const utils_1 = __nccwpck_require__(9277);
|
const utils_1 = __nccwpck_require__(9277);
|
||||||
const pullRequests_1 = __nccwpck_require__(1516);
|
const pullRequests_1 = __nccwpck_require__(1516);
|
||||||
const regexUtils_1 = __nccwpck_require__(7793);
|
const regexUtils_1 = __nccwpck_require__(7793);
|
||||||
|
const types_1 = __nccwpck_require__(3616);
|
||||||
const regexUtils_2 = __nccwpck_require__(4826);
|
const regexUtils_2 = __nccwpck_require__(4826);
|
||||||
const EMPTY_MAP = new Map();
|
|
||||||
let CLEAR = false;
|
let CLEAR = false;
|
||||||
function clear() {
|
function clear() {
|
||||||
CLEAR = true;
|
CLEAR = true;
|
||||||
@@ -2575,7 +2578,7 @@ function buildChangelog(diffInfo, origPrs, options) {
|
|||||||
let prs = origPrs;
|
let prs = origPrs;
|
||||||
if (prs.length === 0) {
|
if (prs.length === 0) {
|
||||||
core.warning(`⚠️ No pull requests found`);
|
core.warning(`⚠️ No pull requests found`);
|
||||||
const result = replaceEmptyTemplate(options.configuration.empty_template, options);
|
const result = renderEmptyChangelogTemplate(options.configuration.empty_template, options);
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2664,36 +2667,75 @@ function buildChangelog(diffInfo, origPrs, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// keep reference for the placeholder values
|
const groupedPlaceholders = (0, utils_1.groupPlaceholders)(config.custom_placeholders || []);
|
||||||
const placeholders = new Map();
|
const customPlaceholdersTemplateContext = new types_1.GroupedTemplateContext();
|
||||||
for (const ph of config.custom_placeholders || []) {
|
|
||||||
(0, utils_1.createOrSet)(placeholders, ph.source, ph);
|
|
||||||
}
|
|
||||||
const placeholderPrMap = new Map();
|
|
||||||
const validatedTransformers = validateTransformers(config.transformers);
|
const validatedTransformers = validateTransformers(config.transformers);
|
||||||
const transformedMap = new Map();
|
core.info(`ℹ️ Using ${validatedTransformers.length} transformers to rewrite content`);
|
||||||
// convert PRs to their text representation
|
if (validatedTransformers.length > 0) {
|
||||||
for (const pr of prs) {
|
for (const pr of prs) {
|
||||||
transformedMap.set(pr, transform(fillPrTemplate(pr, config.pr_template, placeholders, placeholderPrMap, config), validatedTransformers));
|
const prAsObject = pr;
|
||||||
|
transformObject(prAsObject, validatedTransformers);
|
||||||
|
}
|
||||||
|
core.info(`✒️ Transformed ${prs.length} pull requests`);
|
||||||
}
|
}
|
||||||
core.info(`ℹ️ Used ${validatedTransformers.length} transformers to adjust message`);
|
const prInfoMap = buildInfoMapAndFillPlaceholderContext(prs, config.pr_template, groupedPlaceholders, customPlaceholdersTemplateContext, config);
|
||||||
core.info(`✒️ Wrote messages for ${prs.length} pull requests`);
|
|
||||||
// bring PRs into the order of categories
|
// bring PRs into the order of categories
|
||||||
const categories = config.categories;
|
const categories = config.categories;
|
||||||
const ignoredLabels = config.ignore_labels;
|
|
||||||
const flatCategories = flatten(config.categories);
|
const flatCategories = flatten(config.categories);
|
||||||
const categorizedPrs = [];
|
|
||||||
const ignoredPrs = [];
|
|
||||||
const openPrs = [];
|
|
||||||
const uncategorizedPrs = [];
|
|
||||||
// set-up the category object
|
// set-up the category object
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
if (CLEAR || !category.entries) {
|
if (CLEAR || !category.entries) {
|
||||||
category.entries = [];
|
category.entries = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const prStrings = buildPrStringsAndFillCategoryEntries(prInfoMap, config.ignore_labels, categories, flatCategories);
|
||||||
|
core.info(`ℹ️ Ordered all pull requests into ${categories.length} categories`);
|
||||||
|
// serialize and provide the categorized content as json
|
||||||
|
const transformedCategorized = buildCategorizedOutput(flatCategories);
|
||||||
|
core.setOutput('categorized', JSON.stringify(transformedCategorized));
|
||||||
|
// construct final changelog
|
||||||
|
const changelogStrings = buildChangelogStrings(flatCategories, prStrings);
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.categorized.length} categorized pull requests down`);
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.uncategorized.length} non categorized pull requests down`);
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.open.length} open pull requests down`);
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.ignored.length} ignored pull requests down`);
|
||||||
|
core.setOutput('categorized_prs', changelogStrings.categorized.length);
|
||||||
|
core.setOutput('uncategorized_prs', changelogStrings.uncategorized.length);
|
||||||
|
core.setOutput('open_prs', changelogStrings.open.length);
|
||||||
|
core.setOutput('ignored_prs', changelogStrings.ignored.length);
|
||||||
|
// collect all contributors
|
||||||
|
const contributorsSet = new Set(prs.map(pr => `@${pr.author}`));
|
||||||
|
const contributorsArray = Array.from(contributorsSet);
|
||||||
|
const contributorsString = contributorsArray.join(', ');
|
||||||
|
const externalContributorString = contributorsArray.filter(value => value !== options.owner).join(', ');
|
||||||
|
core.setOutput('contributors', JSON.stringify(contributorsSet));
|
||||||
|
const releaseNotesTemplateContext = buildReleaseNotesTemplateContext(changelogStrings, contributorsString, externalContributorString, prStrings, diffInfo, options);
|
||||||
|
let renderedReleaseNotesTemplate = renderTemplateAndFillPlaceholderContext(config.template, releaseNotesTemplateContext, groupedPlaceholders, customPlaceholdersTemplateContext, config);
|
||||||
|
renderedReleaseNotesTemplate = renderTemplateWithContext(renderedReleaseNotesTemplate, customPlaceholdersTemplateContext, config);
|
||||||
|
renderedReleaseNotesTemplate = cleanupPrPlaceholders(renderedReleaseNotesTemplate, groupedPlaceholders);
|
||||||
|
renderedReleaseNotesTemplate = cleanupPlaceholders(renderedReleaseNotesTemplate);
|
||||||
|
core.info(`ℹ️ Filled template`);
|
||||||
|
core.endGroup();
|
||||||
|
return renderedReleaseNotesTemplate;
|
||||||
|
}
|
||||||
|
function buildInfoMapAndFillPlaceholderContext(prData, template, groupedPlaceholders, customPlaceholdersTemplateContext, config) {
|
||||||
|
const infoMap = new Map();
|
||||||
|
for (const pr of prData) {
|
||||||
|
const [prTemplateContext, prArrayTemplateContext] = buildPrTemplateContext(pr);
|
||||||
|
let renderedTemplate = template;
|
||||||
|
renderedTemplate = renderTemplateAndFillPlaceholderContext(renderedTemplate, prArrayTemplateContext, groupedPlaceholders, customPlaceholdersTemplateContext, config);
|
||||||
|
renderedTemplate = renderTemplateAndFillPlaceholderContext(renderedTemplate, prTemplateContext, groupedPlaceholders, customPlaceholdersTemplateContext, config);
|
||||||
|
infoMap.set(pr, renderedTemplate);
|
||||||
|
}
|
||||||
|
return infoMap;
|
||||||
|
}
|
||||||
|
function buildPrStringsAndFillCategoryEntries(prInfoMap, ignoredLabels, categories, flatCategories) {
|
||||||
|
const categorizedPrs = [];
|
||||||
|
const ignoredPrs = [];
|
||||||
|
const openPrs = [];
|
||||||
|
const uncategorizedPrs = [];
|
||||||
// bring elements in order
|
// bring elements in order
|
||||||
prLoop: for (const [pr, body] of transformedMap) {
|
prLoop: for (const [pr, body] of prInfoMap) {
|
||||||
if ((0, utils_1.haveCommonElementsArr)(ignoredLabels.map(lbl => lbl.toLocaleLowerCase('en')), pr.labels)) {
|
if ((0, utils_1.haveCommonElementsArr)(ignoredLabels.map(lbl => lbl.toLocaleLowerCase('en')), pr.labels)) {
|
||||||
ignoredPrs.push(body);
|
ignoredPrs.push(body);
|
||||||
continue;
|
continue;
|
||||||
@@ -2712,12 +2754,12 @@ function buildChangelog(diffInfo, origPrs, options) {
|
|||||||
if (!matchedOnce) {
|
if (!matchedOnce) {
|
||||||
// we allow to have pull requests included in an "uncategorized" category
|
// we allow to have pull requests included in an "uncategorized" category
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
const pullRequests = category.entries || [];
|
category.entries = category.entries || [];
|
||||||
if ((category.labels === undefined || category.labels.length === 0) && category.rules === undefined) {
|
if ((category.labels === undefined || category.labels.length === 0) && category.rules === undefined) {
|
||||||
// check if any exclude label matches for the "uncategorized" category
|
// check if any exclude label matches for the "uncategorized" category
|
||||||
if (category.exclude_labels !== undefined) {
|
if (category.exclude_labels !== undefined) {
|
||||||
if (!(0, utils_1.haveCommonElementsArr)(category.exclude_labels.map(lbl => lbl.toLocaleLowerCase('en')), pr.labels)) {
|
if (!(0, utils_1.haveCommonElementsArr)(category.exclude_labels.map(lbl => lbl.toLocaleLowerCase('en')), pr.labels)) {
|
||||||
pullRequests.push(body);
|
category.entries.push(body);
|
||||||
}
|
}
|
||||||
else if (core.isDebug()) {
|
else if (core.isDebug()) {
|
||||||
const excludeLabels = JSON.stringify(category.exclude_labels);
|
const excludeLabels = JSON.stringify(category.exclude_labels);
|
||||||
@@ -2725,7 +2767,7 @@ function buildChangelog(diffInfo, origPrs, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pullRequests.push(body);
|
category.entries.push(body);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2737,98 +2779,95 @@ function buildChangelog(diffInfo, origPrs, options) {
|
|||||||
categorizedPrs.push(body);
|
categorizedPrs.push(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.info(`ℹ️ Ordered all pull requests into ${categories.length} categories`);
|
const prStrings = {
|
||||||
// serialize and provide the categorized content as json
|
categorizedList: categorizedPrs,
|
||||||
|
uncategorizedList: uncategorizedPrs,
|
||||||
|
openList: openPrs,
|
||||||
|
ignoredList: ignoredPrs
|
||||||
|
};
|
||||||
|
return prStrings;
|
||||||
|
}
|
||||||
|
function buildChangelogStrings(flatCategories, prStrings) {
|
||||||
|
const { categorizedList, uncategorizedList, openList, ignoredList } = prStrings;
|
||||||
|
let changelogCategorized = '';
|
||||||
|
for (const category of flatCategories) {
|
||||||
|
const pullRequests = category.entries || [];
|
||||||
|
changelogCategorized += buildCategorizedChangelogString(category, pullRequests);
|
||||||
|
}
|
||||||
|
if (core.isDebug()) {
|
||||||
|
for (const pr of categorizedList) {
|
||||||
|
core.debug(` ${pr}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let changelogUncategorized = '';
|
||||||
|
for (const pr of uncategorizedList) {
|
||||||
|
changelogUncategorized = `${changelogUncategorized + pr}\n`;
|
||||||
|
}
|
||||||
|
if (core.isDebug()) {
|
||||||
|
for (const pr of uncategorizedList) {
|
||||||
|
core.debug(` ${pr}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let changelogOpen = '';
|
||||||
|
if (openList.length > 0) {
|
||||||
|
for (const pr of openList) {
|
||||||
|
changelogOpen = `${changelogOpen + pr}\n`;
|
||||||
|
}
|
||||||
|
if (core.isDebug()) {
|
||||||
|
for (const pr of openList) {
|
||||||
|
core.debug(` ${pr}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let changelogIgnored = '';
|
||||||
|
for (const pr of ignoredList) {
|
||||||
|
changelogIgnored = `${changelogIgnored + pr}\n`;
|
||||||
|
}
|
||||||
|
if (core.isDebug()) {
|
||||||
|
for (const pr of ignoredList) {
|
||||||
|
core.debug(` ${pr}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const changelogStrings = {
|
||||||
|
categorized: changelogCategorized,
|
||||||
|
uncategorized: changelogUncategorized,
|
||||||
|
open: changelogOpen,
|
||||||
|
ignored: changelogIgnored
|
||||||
|
};
|
||||||
|
return changelogStrings;
|
||||||
|
}
|
||||||
|
function buildReleaseNotesTemplateContext(changelogStrings, contributorsString, externalContributorString, prStrings, diffInfo, options) {
|
||||||
|
const { categorized: changelogCategorized, uncategorized: changelogUncategorized, open: changelogOpen, ignored: changelogIgnored } = changelogStrings;
|
||||||
|
const { categorizedList, uncategorizedList, openList, ignoredList } = prStrings;
|
||||||
|
let releaseNotesTemplateContext = new types_1.TemplateContext();
|
||||||
|
releaseNotesTemplateContext.set('CHANGELOG', changelogCategorized);
|
||||||
|
releaseNotesTemplateContext.set('UNCATEGORIZED', changelogUncategorized);
|
||||||
|
releaseNotesTemplateContext.set('OPEN', changelogOpen);
|
||||||
|
releaseNotesTemplateContext.set('IGNORED', changelogIgnored);
|
||||||
|
// fill special collected contributors
|
||||||
|
releaseNotesTemplateContext.set('CONTRIBUTORS', contributorsString);
|
||||||
|
releaseNotesTemplateContext.set('EXTERNAL_CONTRIBUTORS', externalContributorString);
|
||||||
|
// fill other placeholders
|
||||||
|
releaseNotesTemplateContext.set('CATEGORIZED_COUNT', categorizedList.length.toString());
|
||||||
|
releaseNotesTemplateContext.set('UNCATEGORIZED_COUNT', uncategorizedList.length.toString());
|
||||||
|
releaseNotesTemplateContext.set('OPEN_COUNT', openList.length.toString());
|
||||||
|
releaseNotesTemplateContext.set('IGNORED_COUNT', ignoredList.length.toString());
|
||||||
|
// code change placeholders
|
||||||
|
releaseNotesTemplateContext.set('CHANGED_FILES', diffInfo.changedFiles.toString());
|
||||||
|
releaseNotesTemplateContext.set('ADDITIONS', diffInfo.additions.toString());
|
||||||
|
releaseNotesTemplateContext.set('DELETIONS', diffInfo.deletions.toString());
|
||||||
|
releaseNotesTemplateContext.set('CHANGES', diffInfo.changes.toString());
|
||||||
|
releaseNotesTemplateContext.set('COMMITS', diffInfo.commits.toString());
|
||||||
|
const coreReleasesNotesContext = buildCoreReleaseNotesTemplateContext(options);
|
||||||
|
releaseNotesTemplateContext = (0, utils_1.mergeMaps)(releaseNotesTemplateContext, coreReleasesNotesContext);
|
||||||
|
return releaseNotesTemplateContext;
|
||||||
|
}
|
||||||
|
function buildCategorizedOutput(flatCategories) {
|
||||||
const transformedCategorized = {};
|
const transformedCategorized = {};
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
Object.assign(transformedCategorized, { [category.key || category.title]: category.entries });
|
Object.assign(transformedCategorized, { [category.key || category.title]: category.entries });
|
||||||
}
|
}
|
||||||
core.setOutput('categorized', JSON.stringify(transformedCategorized));
|
return transformedCategorized;
|
||||||
// construct final changelog
|
|
||||||
let changelog = '';
|
|
||||||
for (const category of flatCategories) {
|
|
||||||
const pullRequests = category.entries || [];
|
|
||||||
changelog = attachCategoryChangelog(changelog, category, pullRequests);
|
|
||||||
}
|
|
||||||
core.info(`✒️ Wrote ${categorizedPrs.length} categorized pull requests down`);
|
|
||||||
if (core.isDebug()) {
|
|
||||||
for (const pr of categorizedPrs) {
|
|
||||||
core.debug(` ${pr}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.setOutput('categorized_prs', categorizedPrs.length);
|
|
||||||
let changelogUncategorized = '';
|
|
||||||
for (const pr of uncategorizedPrs) {
|
|
||||||
changelogUncategorized = `${changelogUncategorized + pr}\n`;
|
|
||||||
}
|
|
||||||
core.info(`✒️ Wrote ${uncategorizedPrs.length} non categorized pull requests down`);
|
|
||||||
if (core.isDebug()) {
|
|
||||||
for (const pr of uncategorizedPrs) {
|
|
||||||
core.debug(` ${pr}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.setOutput('uncategorized_prs', uncategorizedPrs.length);
|
|
||||||
let changelogOpen = '';
|
|
||||||
if (openPrs.length > 0) {
|
|
||||||
for (const pr of openPrs) {
|
|
||||||
changelogOpen = `${changelogOpen + pr}\n`;
|
|
||||||
}
|
|
||||||
core.info(`✒️ Wrote ${openPrs.length} open pull requests down`);
|
|
||||||
if (core.isDebug()) {
|
|
||||||
for (const pr of openPrs) {
|
|
||||||
core.debug(` ${pr}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.setOutput('open_prs', openPrs.length);
|
|
||||||
}
|
|
||||||
let changelogIgnored = '';
|
|
||||||
for (const pr of ignoredPrs) {
|
|
||||||
changelogIgnored = `${changelogIgnored + pr}\n`;
|
|
||||||
}
|
|
||||||
if (core.isDebug()) {
|
|
||||||
for (const pr of ignoredPrs) {
|
|
||||||
core.debug(` ${pr}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(`✒️ Wrote ${ignoredPrs.length} ignored pull requests down`);
|
|
||||||
// collect all contributors
|
|
||||||
const contributorsSet = new Set();
|
|
||||||
for (const pr of prs) {
|
|
||||||
contributorsSet.add(`@${pr.author}`);
|
|
||||||
}
|
|
||||||
const contributorsArray = Array.from(contributorsSet);
|
|
||||||
const contributorsString = contributorsArray.join(', ');
|
|
||||||
const externalContributorString = contributorsArray.filter(value => value !== options.owner).join(', ');
|
|
||||||
core.setOutput('contributors', JSON.stringify(contributorsSet));
|
|
||||||
// fill template
|
|
||||||
const placeholderMap = new Map();
|
|
||||||
placeholderMap.set('CHANGELOG', changelog);
|
|
||||||
placeholderMap.set('UNCATEGORIZED', changelogUncategorized);
|
|
||||||
placeholderMap.set('OPEN', changelogOpen);
|
|
||||||
placeholderMap.set('IGNORED', changelogIgnored);
|
|
||||||
// fill special collected contributors
|
|
||||||
placeholderMap.set('CONTRIBUTORS', contributorsString);
|
|
||||||
placeholderMap.set('EXTERNAL_CONTRIBUTORS', externalContributorString);
|
|
||||||
// fill other placeholders
|
|
||||||
placeholderMap.set('CATEGORIZED_COUNT', categorizedPrs.length.toString());
|
|
||||||
placeholderMap.set('UNCATEGORIZED_COUNT', uncategorizedPrs.length.toString());
|
|
||||||
placeholderMap.set('OPEN_COUNT', openPrs.length.toString());
|
|
||||||
placeholderMap.set('IGNORED_COUNT', ignoredPrs.length.toString());
|
|
||||||
// code change placeholders
|
|
||||||
placeholderMap.set('CHANGED_FILES', diffInfo.changedFiles.toString());
|
|
||||||
placeholderMap.set('ADDITIONS', diffInfo.additions.toString());
|
|
||||||
placeholderMap.set('DELETIONS', diffInfo.deletions.toString());
|
|
||||||
placeholderMap.set('CHANGES', diffInfo.changes.toString());
|
|
||||||
placeholderMap.set('COMMITS', diffInfo.commits.toString());
|
|
||||||
fillAdditionalPlaceholders(options, placeholderMap);
|
|
||||||
let transformedChangelog = config.template;
|
|
||||||
transformedChangelog = replacePlaceholders(transformedChangelog, EMPTY_MAP, placeholderMap, placeholders, placeholderPrMap, config);
|
|
||||||
transformedChangelog = replacePrPlaceholders(transformedChangelog, placeholderPrMap, config);
|
|
||||||
transformedChangelog = cleanupPrPlaceholders(transformedChangelog, placeholders);
|
|
||||||
transformedChangelog = cleanupPlaceholders(transformedChangelog);
|
|
||||||
core.info(`ℹ️ Filled template`);
|
|
||||||
core.endGroup();
|
|
||||||
return transformedChangelog;
|
|
||||||
}
|
}
|
||||||
function recursiveCategorizePr(category, pr, body) {
|
function recursiveCategorizePr(category, pr, body) {
|
||||||
let matched = false;
|
let matched = false;
|
||||||
@@ -2844,10 +2883,10 @@ function recursiveCategorizePr(category, pr, body) {
|
|||||||
}
|
}
|
||||||
// if consumed we don't handle it anymore, as it was matched in a child, don't handle anymore
|
// if consumed we don't handle it anymore, as it was matched in a child, don't handle anymore
|
||||||
if (!consumed && !matched) {
|
if (!consumed && !matched) {
|
||||||
const pullRequests = category.entries || [];
|
category.entries = category.entries || [];
|
||||||
matched = matchesParent;
|
matched = matchesParent;
|
||||||
if (matched) {
|
if (matched) {
|
||||||
pullRequests.push(body); // if matched add the PR to the list
|
category.entries.push(body); // if matched add the PR to the list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (matched && category.consume) {
|
if (matched && category.consume) {
|
||||||
@@ -2898,119 +2937,130 @@ function categorizePr(category, pr) {
|
|||||||
}
|
}
|
||||||
return matched;
|
return matched;
|
||||||
}
|
}
|
||||||
function attachCategoryChangelog(changelog, category, pullRequests) {
|
function buildCategorizedChangelogString(category, pullRequests) {
|
||||||
|
let categorizedString = '';
|
||||||
if (pullRequests.length > 0 || hasChildWithEntries(category)) {
|
if (pullRequests.length > 0 || hasChildWithEntries(category)) {
|
||||||
if (category.title) {
|
if (category.title) {
|
||||||
changelog = `${changelog + category.title}\n\n`;
|
categorizedString = `${categorizedString + category.title}\n\n`;
|
||||||
}
|
}
|
||||||
for (const pr of pullRequests) {
|
for (const pr of pullRequests) {
|
||||||
changelog = `${changelog + pr}\n`;
|
categorizedString = `${categorizedString + pr}\n`;
|
||||||
}
|
}
|
||||||
changelog = `${changelog}\n`; // add space between sections
|
categorizedString = `${categorizedString}\n`; // add space between sections
|
||||||
}
|
}
|
||||||
else if (category.empty_content !== undefined) {
|
else if (category.empty_content !== undefined) {
|
||||||
if (category.title) {
|
if (category.title) {
|
||||||
changelog = `${changelog + category.title}\n\n`;
|
categorizedString = `${categorizedString + category.title}\n\n`;
|
||||||
}
|
}
|
||||||
changelog = `${changelog + category.empty_content}\n\n`;
|
categorizedString = `${categorizedString + category.empty_content}\n\n`;
|
||||||
}
|
}
|
||||||
return changelog;
|
return categorizedString;
|
||||||
}
|
}
|
||||||
function replaceEmptyTemplate(template, options) {
|
function renderEmptyChangelogTemplate(template, options) {
|
||||||
const placeholders = new Map();
|
const placeholders = new Map();
|
||||||
for (const ph of options.configuration.custom_placeholders || []) {
|
for (const ph of options.configuration.custom_placeholders || []) {
|
||||||
(0, utils_1.createOrSet)(placeholders, ph.source, ph);
|
(0, utils_1.createOrSet)(placeholders, ph.source, ph);
|
||||||
}
|
}
|
||||||
const placeholderMap = new Map();
|
const releaseNotesTemplateContext = buildCoreReleaseNotesTemplateContext(options);
|
||||||
fillAdditionalPlaceholders(options, placeholderMap);
|
const renderedEmptyChangelogTemplate = renderTemplateAndFillPlaceholderContext(template, releaseNotesTemplateContext, placeholders, undefined, options.configuration);
|
||||||
return replacePlaceholders(template, EMPTY_MAP, placeholderMap, placeholders, undefined, options.configuration);
|
return renderedEmptyChangelogTemplate;
|
||||||
}
|
}
|
||||||
function fillAdditionalPlaceholders(options, placeholderMap /* placeholderKey and original value */) {
|
function buildCoreReleaseNotesTemplateContext(options) {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
placeholderMap.set('OWNER', options.owner);
|
const templateContext = new types_1.TemplateContext();
|
||||||
placeholderMap.set('REPO', options.repo);
|
templateContext.set('OWNER', options.owner);
|
||||||
placeholderMap.set('FROM_TAG', options.fromTag.name);
|
templateContext.set('REPO', options.repo);
|
||||||
placeholderMap.set('FROM_TAG_DATE', ((_a = options.fromTag.date) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
|
templateContext.set('FROM_TAG', options.fromTag.name);
|
||||||
placeholderMap.set('TO_TAG', options.toTag.name);
|
templateContext.set('FROM_TAG_DATE', ((_a = options.fromTag.date) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
|
||||||
placeholderMap.set('TO_TAG_DATE', ((_b = options.toTag.date) === null || _b === void 0 ? void 0 : _b.toISOString()) || '');
|
templateContext.set('TO_TAG', options.toTag.name);
|
||||||
|
templateContext.set('TO_TAG_DATE', ((_b = options.toTag.date) === null || _b === void 0 ? void 0 : _b.toISOString()) || '');
|
||||||
const fromDate = options.fromTag.date;
|
const fromDate = options.fromTag.date;
|
||||||
const toDate = options.toTag.date;
|
const toDate = options.toTag.date;
|
||||||
if (fromDate !== undefined && toDate !== undefined) {
|
if (fromDate !== undefined && toDate !== undefined) {
|
||||||
placeholderMap.set('DAYS_SINCE', toDate.diff(fromDate, 'days').toString() || '');
|
templateContext.set('DAYS_SINCE', toDate.diff(fromDate, 'days').toString() || '');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
placeholderMap.set('DAYS_SINCE', '');
|
templateContext.set('DAYS_SINCE', '');
|
||||||
}
|
}
|
||||||
placeholderMap.set('RELEASE_DIFF', `${options.repositoryUtils.homeUrl}/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`);
|
templateContext.set('RELEASE_DIFF', `${options.repositoryUtils.homeUrl}/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`);
|
||||||
|
return templateContext;
|
||||||
}
|
}
|
||||||
function fillPrTemplate(pr, template, placeholders /* placeholders to apply */, placeholderPrMap /* map to keep replaced placeholder values with their key */, configuration) {
|
function buildPrTemplateContext(pr) {
|
||||||
var _a, _b, _c, _d, _e, _f;
|
var _a, _b, _c, _d, _e, _f;
|
||||||
const arrayPlaceholderMap = new Map();
|
const prTemplateContext = new types_1.TemplateContext();
|
||||||
fillReviewPlaceholders(arrayPlaceholderMap, 'REVIEWS', pr.reviews || []);
|
prTemplateContext.set('NUMBER', pr.number.toString());
|
||||||
fillChildPrPlaceholders(arrayPlaceholderMap, 'REFERENCED', pr.childPrs || []);
|
prTemplateContext.set('TITLE', pr.title);
|
||||||
const placeholderMap = new Map();
|
prTemplateContext.set('URL', pr.htmlURL);
|
||||||
placeholderMap.set('NUMBER', pr.number.toString());
|
prTemplateContext.set('STATUS', pr.status);
|
||||||
placeholderMap.set('TITLE', pr.title);
|
prTemplateContext.set('CREATED_AT', pr.createdAt.toISOString());
|
||||||
placeholderMap.set('URL', pr.htmlURL);
|
prTemplateContext.set('MERGED_AT', ((_a = pr.mergedAt) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
|
||||||
placeholderMap.set('STATUS', pr.status);
|
prTemplateContext.set('MERGE_SHA', pr.mergeCommitSha);
|
||||||
placeholderMap.set('CREATED_AT', pr.createdAt.toISOString());
|
prTemplateContext.set('AUTHOR', pr.author);
|
||||||
placeholderMap.set('MERGED_AT', ((_a = pr.mergedAt) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
|
prTemplateContext.set('AUTHOR_NAME', pr.authorName || '');
|
||||||
placeholderMap.set('MERGE_SHA', pr.mergeCommitSha);
|
prTemplateContext.set('LABELS', ((_c = (_b = [...pr.labels]) === null || _b === void 0 ? void 0 : _b.filter(l => !l.startsWith('--rcba-'))) === null || _c === void 0 ? void 0 : _c.join(', ')) || '');
|
||||||
placeholderMap.set('AUTHOR', pr.author);
|
prTemplateContext.set('MILESTONE', pr.milestone || '');
|
||||||
placeholderMap.set('AUTHOR_NAME', pr.authorName || '');
|
prTemplateContext.set('BODY', pr.body);
|
||||||
placeholderMap.set('LABELS', ((_c = (_b = [...pr.labels]) === null || _b === void 0 ? void 0 : _b.filter(l => !l.startsWith('--rcba-'))) === null || _c === void 0 ? void 0 : _c.join(', ')) || '');
|
prTemplateContext.set('ASSIGNEES', ((_d = pr.assignees) === null || _d === void 0 ? void 0 : _d.join(', ')) || '');
|
||||||
placeholderMap.set('MILESTONE', pr.milestone || '');
|
prTemplateContext.set('REVIEWERS', ((_e = pr.requestedReviewers) === null || _e === void 0 ? void 0 : _e.join(', ')) || '');
|
||||||
placeholderMap.set('BODY', pr.body);
|
prTemplateContext.set('APPROVERS', ((_f = pr.approvedReviewers) === null || _f === void 0 ? void 0 : _f.join(', ')) || '');
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'ASSIGNEES', pr.assignees || []);
|
prTemplateContext.set('BRANCH', pr.branch || '');
|
||||||
placeholderMap.set('ASSIGNEES', ((_d = pr.assignees) === null || _d === void 0 ? void 0 : _d.join(', ')) || '');
|
prTemplateContext.set('BASE_BRANCH', pr.baseBranch);
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'REVIEWERS', pr.requestedReviewers || []);
|
const prArrayTemplateContext = new types_1.TemplateContext();
|
||||||
placeholderMap.set('REVIEWERS', ((_e = pr.requestedReviewers) === null || _e === void 0 ? void 0 : _e.join(', ')) || '');
|
fillReviewPlaceholders(prArrayTemplateContext, 'REVIEWS', pr.reviews || []);
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'APPROVERS', pr.approvedReviewers || []);
|
fillChildPrPlaceholders(prArrayTemplateContext, 'REFERENCED', pr.childPrs || []);
|
||||||
placeholderMap.set('APPROVERS', ((_f = pr.approvedReviewers) === null || _f === void 0 ? void 0 : _f.join(', ')) || '');
|
fillArrayPlaceholders(prArrayTemplateContext, 'ASSIGNEES', pr.assignees || []);
|
||||||
placeholderMap.set('BRANCH', pr.branch || '');
|
fillArrayPlaceholders(prArrayTemplateContext, 'REVIEWERS', pr.requestedReviewers || []);
|
||||||
placeholderMap.set('BASE_BRANCH', pr.baseBranch);
|
fillArrayPlaceholders(prArrayTemplateContext, 'APPROVERS', pr.approvedReviewers || []);
|
||||||
return replacePlaceholders(template, arrayPlaceholderMap, placeholderMap, placeholders, placeholderPrMap, configuration);
|
return [prTemplateContext, prArrayTemplateContext];
|
||||||
}
|
}
|
||||||
function replacePlaceholders(template, arrayPlaceholderMap /* arrayPlaceholderKey and original value */, placeholderMap /* placeholderKey and original value */, placeholders /* placeholders to apply */, placeholderPrMap /* map to keep replaced placeholder values with their key */, configuration) {
|
function renderTemplateAndFillPlaceholderContext(template, templateContext /* placeholderKey and original value */, customPlaceholders /* placeholders to apply */, customPlaceholdersTemplateContext /* map to keep replaced placeholder values with their key */, configuration) {
|
||||||
let transformed = template;
|
let transformed = template;
|
||||||
// replace array placeholders first
|
const trimValues = configuration.trim_values;
|
||||||
for (const [key, value] of arrayPlaceholderMap) {
|
|
||||||
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap, configuration);
|
|
||||||
}
|
|
||||||
// replace traditional placeholders
|
// replace traditional placeholders
|
||||||
for (const [key, value] of placeholderMap) {
|
for (const [key, value] of templateContext) {
|
||||||
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap, configuration);
|
transformed = transformed.replaceAll(`#{{${key}}}`, trimValues ? value.trim() : value);
|
||||||
}
|
const extractedValues = extractPlaceholderValuesAndFillPlaceholderContext(key, value, customPlaceholders, customPlaceholdersTemplateContext);
|
||||||
return transformed;
|
for (const [placeholderName, extractedValue] of extractedValues) {
|
||||||
}
|
transformed = transformed.replaceAll(`#{{${placeholderName}}}`, trimValues ? extractedValue.trim() : extractedValue);
|
||||||
function handlePlaceholder(template, key, value, placeholders /* placeholders to apply */, placeholderPrMap /* map to keep replaced placeholder values with their key */, configuration) {
|
|
||||||
let transformed = template.replaceAll(`#{{${key}}}`, configuration.trim_values ? value.trim() : value);
|
|
||||||
// replace custom placeholders
|
|
||||||
const phs = placeholders.get(key);
|
|
||||||
if (phs) {
|
|
||||||
for (const placeholder of phs) {
|
|
||||||
const transformer = (0, regexUtils_1.validateRegex)(placeholder.transformer);
|
|
||||||
if (transformer === null || transformer === void 0 ? void 0 : transformer.pattern) {
|
|
||||||
const extractedValue = (0, regexUtils_1.transformStringToOptionalValue)(value, transformer);
|
|
||||||
// note: `.replace` will return the full string again if there was no match
|
|
||||||
// note: This is mostly backwards compatibility
|
|
||||||
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
|
|
||||||
if (placeholderPrMap) {
|
|
||||||
(0, utils_1.createOrSet)(placeholderPrMap, placeholder.name, extractedValue);
|
|
||||||
}
|
|
||||||
transformed = transformed.replaceAll(`#{{${placeholder.name}}}`, configuration.trim_values ? extractedValue.trim() : extractedValue);
|
|
||||||
if (core.isDebug()) {
|
|
||||||
core.debug(` Custom Placeholder successfully matched data - ${extractedValue} (${placeholder.name})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (core.isDebug() && extractedValue === value) {
|
|
||||||
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
|
function extractPlaceholderValuesAndFillPlaceholderContext(key, value, customPlaceholders, customPlaceholdersTemplateContext) {
|
||||||
|
// Replace custom placeholders
|
||||||
|
const placeholdersForKey = customPlaceholders.get(key);
|
||||||
|
const extractedValues = new types_1.TemplateContext();
|
||||||
|
if (!placeholdersForKey) {
|
||||||
|
return extractedValues;
|
||||||
|
}
|
||||||
|
for (const placeholder of placeholdersForKey) {
|
||||||
|
const extractedValue = extractTransformedValue(value, placeholder);
|
||||||
|
if (extractedValue) {
|
||||||
|
extractedValues.set(placeholder.name, extractedValue);
|
||||||
|
if (customPlaceholdersTemplateContext) {
|
||||||
|
(0, utils_1.createOrSet)(customPlaceholdersTemplateContext, placeholder.name, extractedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extractedValues;
|
||||||
|
}
|
||||||
|
function extractTransformedValue(value, placeholder) {
|
||||||
|
const transformer = (0, regexUtils_1.validateRegex)(placeholder.transformer);
|
||||||
|
if (!(transformer === null || transformer === void 0 ? void 0 : transformer.pattern)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const extractedValue = (0, regexUtils_1.transformStringToOptionalValue)(value, transformer);
|
||||||
|
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
|
||||||
|
if (core.isDebug()) {
|
||||||
|
core.debug(` Custom Placeholder successfully matched data - ${extractedValue} (${placeholder.name})`);
|
||||||
|
}
|
||||||
|
return extractedValue;
|
||||||
|
}
|
||||||
|
if (core.isDebug() && extractedValue === value) {
|
||||||
|
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
function fillArrayPlaceholders(placeholderMap /* placeholderKey and original value */, key, values) {
|
function fillArrayPlaceholders(placeholderMap /* placeholderKey and original value */, key, values) {
|
||||||
if (values.length === 0)
|
if (values.length === 0)
|
||||||
return;
|
return;
|
||||||
@@ -3043,9 +3093,9 @@ function fillChildPrPlaceholders(placeholderMap /* placeholderKey and original v
|
|||||||
placeholderMap.set(`${parentKey}[*].${childKey}`, values.map(value => { var _a; return ((_a = value[childKey]) === null || _a === void 0 ? void 0 : _a.toLocaleString('en')) || ''; }).join(', '));
|
placeholderMap.set(`${parentKey}[*].${childKey}`, values.map(value => { var _a; return ((_a = value[childKey]) === null || _a === void 0 ? void 0 : _a.toLocaleString('en')) || ''; }).join(', '));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function replacePrPlaceholders(template, placeholderPrMap /* map with all pr related custom placeholder values */, configuration) {
|
function renderTemplateWithContext(template, templateContext /* map with all pr related custom placeholder values */, configuration) {
|
||||||
let transformed = template;
|
let transformed = template;
|
||||||
for (const [key, values] of placeholderPrMap) {
|
for (const [key, values] of templateContext) {
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
transformed = transformed.replaceAll(`#{{${key}[${i}]}}`, configuration.trim_values ? values[i].trim() : values[i]);
|
transformed = transformed.replaceAll(`#{{${key}[${i}]}}`, configuration.trim_values ? values[i].trim() : values[i]);
|
||||||
}
|
}
|
||||||
@@ -3069,6 +3119,28 @@ function cleanupPlaceholders(template) {
|
|||||||
}
|
}
|
||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
|
function transformObject(obj, transformers) {
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
if (value === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
if (value.every(item => typeof item === 'string')) {
|
||||||
|
// If the array contains only strings, apply the transformation to each string
|
||||||
|
obj[key] = value.map(item => transform(item, transformers));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If the array contains objects, recursively apply the transformation to each object
|
||||||
|
for (const child of value) {
|
||||||
|
transformObject(child, transformers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof value === 'string') {
|
||||||
|
obj[key] = transform(value, transformers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
function transform(filled, transformers) {
|
function transform(filled, transformers) {
|
||||||
if (transformers.length === 0) {
|
if (transformers.length === 0) {
|
||||||
return filled;
|
return filled;
|
||||||
@@ -3082,8 +3154,7 @@ function transform(filled, transformers) {
|
|||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
function validateTransformers(specifiedTransformers) {
|
function validateTransformers(specifiedTransformers) {
|
||||||
const transformers = specifiedTransformers;
|
return specifiedTransformers
|
||||||
return transformers
|
|
||||||
.map(transformer => {
|
.map(transformer => {
|
||||||
return (0, regexUtils_1.validateRegex)(transformer);
|
return (0, regexUtils_1.validateRegex)(transformer);
|
||||||
})
|
})
|
||||||
@@ -3191,10 +3262,12 @@ exports.parseConfiguration = parseConfiguration;
|
|||||||
exports.mergeConfiguration = mergeConfiguration;
|
exports.mergeConfiguration = mergeConfiguration;
|
||||||
exports.writeOutput = writeOutput;
|
exports.writeOutput = writeOutput;
|
||||||
exports.createOrSet = createOrSet;
|
exports.createOrSet = createOrSet;
|
||||||
|
exports.groupPlaceholders = groupPlaceholders;
|
||||||
exports.haveCommonElements = haveCommonElements;
|
exports.haveCommonElements = haveCommonElements;
|
||||||
exports.haveCommonElementsArr = haveCommonElementsArr;
|
exports.haveCommonElementsArr = haveCommonElementsArr;
|
||||||
exports.haveEveryElements = haveEveryElements;
|
exports.haveEveryElements = haveEveryElements;
|
||||||
exports.haveEveryElementsArr = haveEveryElementsArr;
|
exports.haveEveryElementsArr = haveEveryElementsArr;
|
||||||
|
exports.mergeMaps = mergeMaps;
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const fs = __importStar(__nccwpck_require__(9896));
|
const fs = __importStar(__nccwpck_require__(9896));
|
||||||
const path = __importStar(__nccwpck_require__(6928));
|
const path = __importStar(__nccwpck_require__(6928));
|
||||||
@@ -3429,6 +3502,27 @@ function createOrSet(map, key, value) {
|
|||||||
entry.push(value);
|
entry.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Groups an array of Placeholder objects by their source attribute.
|
||||||
|
*
|
||||||
|
* @param {Placeholder[]} placeholders - An array of Placeholder objects to be grouped.
|
||||||
|
* @returns {Map<string, Placeholder[]>} A map where the key is the source attribute of the Placeholder objects,
|
||||||
|
* and the value is an array of Placeholder objects that share the same source attribute.
|
||||||
|
*/
|
||||||
|
function groupPlaceholders(placeholders) {
|
||||||
|
const map = new Map();
|
||||||
|
for (const ph of placeholders) {
|
||||||
|
const key = ph.source;
|
||||||
|
const entry = map.get(key);
|
||||||
|
if (entry === undefined) {
|
||||||
|
map.set(key, [ph]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry.push(ph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
function haveCommonElements(arr1, arr2) {
|
function haveCommonElements(arr1, arr2) {
|
||||||
return arr1.some(item => arr2.has(item));
|
return arr1.some(item => arr2.has(item));
|
||||||
}
|
}
|
||||||
@@ -3441,6 +3535,9 @@ function haveEveryElements(arr1, arr2) {
|
|||||||
function haveEveryElementsArr(arr1, arr2) {
|
function haveEveryElementsArr(arr1, arr2) {
|
||||||
return haveEveryElements(arr1, new Set(arr2));
|
return haveEveryElements(arr1, new Set(arr2));
|
||||||
}
|
}
|
||||||
|
function mergeMaps(map1, map2) {
|
||||||
|
return new Map([...map1, ...map2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+7
-24
@@ -1,11 +1,6 @@
|
|||||||
import {Extractor, PullConfiguration, Regex, Rule, Sort} from './pr-collector/types'
|
import {Extractor, PullConfiguration, Regex, Rule} from './pr-collector/types'
|
||||||
|
|
||||||
export interface Configuration extends PullConfiguration {
|
export interface Configuration extends 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"
|
|
||||||
template: string
|
template: string
|
||||||
pr_template: string
|
pr_template: string
|
||||||
empty_template: string
|
empty_template: string
|
||||||
@@ -15,8 +10,6 @@ export interface Configuration extends PullConfiguration {
|
|||||||
duplicate_filter?: Extractor // extract an identifier from a PR used to detect duplicates, will keep the last match (depends on `sort`)
|
duplicate_filter?: Extractor // extract an identifier from a PR used to detect duplicates, will keep the last match (depends on `sort`)
|
||||||
reference?: Extractor // extracts a reference from a PR, used to establish parent child relations. This will remove the child from the main PR list.
|
reference?: Extractor // extracts a reference from a PR, used to establish parent child relations. This will remove the child from the main PR list.
|
||||||
transformers: Regex[]
|
transformers: Regex[]
|
||||||
tag_resolver: TagResolver
|
|
||||||
base_branches: string[]
|
|
||||||
custom_placeholders?: Placeholder[]
|
custom_placeholders?: Placeholder[]
|
||||||
trim_values: boolean
|
trim_values: boolean
|
||||||
}
|
}
|
||||||
@@ -63,6 +56,8 @@ export interface Placeholder {
|
|||||||
transformer: Regex // the transformer to use to transform the original placeholder into the custom placeheolder
|
transformer: Regex // the transformer to use to transform the original placeholder into the custom placeheolder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PlaceholderGroup extends Map<string, Placeholder[]> {}
|
||||||
|
|
||||||
export const DefaultConfiguration: Configuration = {
|
export const DefaultConfiguration: Configuration = {
|
||||||
max_tags_to_fetch: 200, // the amount of tags to fetch from the github API
|
max_tags_to_fetch: 200, // the amount of tags to fetch from the github API
|
||||||
max_pull_requests: 200, // the amount of pull requests to process
|
max_pull_requests: 200, // the amount of pull requests to process
|
||||||
@@ -110,14 +105,8 @@ export const DefaultConfiguration: Configuration = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultCommitConfiguration: Configuration = {
|
export const DefaultCommitConfiguration: Configuration = {
|
||||||
max_tags_to_fetch: DefaultConfiguration.max_tags_to_fetch,
|
...DefaultConfiguration,
|
||||||
max_pull_requests: DefaultConfiguration.max_pull_requests,
|
pr_template: '- #{{TITLE}}', // the per PR template to pick
|
||||||
max_back_track_time_days: DefaultConfiguration.max_back_track_time_days,
|
|
||||||
exclude_merge_branches: DefaultConfiguration.exclude_merge_branches,
|
|
||||||
sort: DefaultConfiguration.sort,
|
|
||||||
template: '#{{CHANGELOG}}', // the global template to host the changelog
|
|
||||||
pr_template: '- #{{TITLE}}', // the per PR template to pick for commit based mode
|
|
||||||
empty_template: DefaultConfiguration.empty_template,
|
|
||||||
categories: [
|
categories: [
|
||||||
{
|
{
|
||||||
title: '## 🚀 Features',
|
title: '## 🚀 Features',
|
||||||
@@ -135,17 +124,11 @@ export const DefaultCommitConfiguration: Configuration = {
|
|||||||
title: '## 📦 Other',
|
title: '## 📦 Other',
|
||||||
labels: []
|
labels: []
|
||||||
}
|
}
|
||||||
], // the categories to support for the ordering
|
],
|
||||||
ignore_labels: DefaultConfiguration.ignore_labels,
|
|
||||||
label_extractor: [
|
label_extractor: [
|
||||||
{
|
{
|
||||||
pattern: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\\([\\w\\-\\.]+\\))?(!)?: ([\\w ])+([\\s\\S]*)',
|
pattern: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\\([\\w\\-\\.]+\\))?(!)?: ([\\w ])+([\\s\\S]*)',
|
||||||
target: '$1'
|
target: '$1'
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
transformers: DefaultConfiguration.transformers,
|
|
||||||
tag_resolver: DefaultConfiguration.tag_resolver,
|
|
||||||
base_branches: DefaultConfiguration.base_branches,
|
|
||||||
custom_placeholders: DefaultConfiguration.custom_placeholders,
|
|
||||||
trim_values: DefaultConfiguration.trim_values
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ export interface PullRequestInfo {
|
|||||||
status: 'open' | 'merged'
|
status: 'open' | 'merged'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PullRequestData extends PullRequestInfo {
|
||||||
|
childPrs?: PullRequestInfo[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface CommentInfo {
|
export interface CommentInfo {
|
||||||
id: number
|
id: number
|
||||||
htmlURL: string
|
htmlURL: string
|
||||||
@@ -79,7 +83,7 @@ export class PullRequests {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
||||||
return sortPrs(await this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests))
|
return sortPrsByMergedAt(await this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBetweenDates(
|
async getBetweenDates(
|
||||||
@@ -89,11 +93,11 @@ export class PullRequests {
|
|||||||
toDate: moment.Moment,
|
toDate: moment.Moment,
|
||||||
maxPullRequests: number
|
maxPullRequests: number
|
||||||
): Promise<PullRequestInfo[]> {
|
): Promise<PullRequestInfo[]> {
|
||||||
return sortPrs(await this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests))
|
return sortPrsByMergedAt(await this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
||||||
return sortPrs(await this.repositoryUtils.getOpen(owner, repo, maxPullRequests))
|
return sortPrsByMergedAt(await this.repositoryUtils.getOpen(owner, repo, maxPullRequests))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
|
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
|
||||||
@@ -221,7 +225,7 @@ export class PullRequests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] {
|
function sortPrsByMergedAt(pullRequests: PullRequestInfo[]): PullRequestInfo[] {
|
||||||
return sortPullRequests(pullRequests, {
|
return sortPullRequests(pullRequests, {
|
||||||
order: 'ASC',
|
order: 'ASC',
|
||||||
on_property: 'mergedAt'
|
on_property: 'mergedAt'
|
||||||
|
|||||||
@@ -58,3 +58,20 @@ export interface RegexTransformer {
|
|||||||
method?: 'replace' | 'replaceAll' | 'match' | 'regexr'
|
method?: 'replace' | 'replaceAll' | 'match' | 'regexr'
|
||||||
onEmpty?: string
|
onEmpty?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GroupedTemplateContext extends Map<string, string[]> {}
|
||||||
|
export class TemplateContext extends Map<string, string> {}
|
||||||
|
|
||||||
|
export type ChangelogStrings = {
|
||||||
|
categorized: string
|
||||||
|
uncategorized: string
|
||||||
|
open: string
|
||||||
|
ignored: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PrStrings = {
|
||||||
|
categorizedList: string[]
|
||||||
|
uncategorizedList: string[]
|
||||||
|
openList: string[]
|
||||||
|
ignoredList: string[]
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import {validateRegex} from './pr-collector/regexUtils'
|
|||||||
/**
|
/**
|
||||||
* Checks if any of the rules match the given PR
|
* Checks if any of the rules match the given PR
|
||||||
*/
|
*/
|
||||||
export function matchesRules(rules: Rule[], pr: PullRequestInfo, exhaustive: Boolean): boolean {
|
export function matchesRules(rules: Rule[], pr: PullRequestInfo, exhaustive: boolean): boolean {
|
||||||
const transformers: RegexTransformer[] = rules.map(rule => validateRegex(rule)).filter(t => t !== null) as RegexTransformer[]
|
const transformers: RegexTransformer[] = rules.map(rule => validateRegex(rule)).filter(t => t !== null) as RegexTransformer[]
|
||||||
if (exhaustive) {
|
if (exhaustive) {
|
||||||
return transformers.every(transformer => {
|
return transformers.every(transformer => {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Configuration} from './configuration'
|
import {Configuration} from './configuration'
|
||||||
import {checkExportedData, writeCacheData} from './utils'
|
import {checkExportedData, writeCacheData} from './utils'
|
||||||
import {buildChangelog, PullRequestData} from './transform'
|
import {buildChangelog} from './transform'
|
||||||
import {PullRequestCollector} from './pr-collector/prCollector'
|
import {PullRequestCollector} from './pr-collector/prCollector'
|
||||||
import {failOrError} from './pr-collector/utils'
|
import {failOrError} from './pr-collector/utils'
|
||||||
import {TagInfo} from './pr-collector/tags'
|
import {TagInfo} from './pr-collector/tags'
|
||||||
import {DiffInfo} from './pr-collector/commits'
|
import {DiffInfo} from './pr-collector/commits'
|
||||||
import {PullRequestInfo} from './pr-collector/pullRequests'
|
import {PullRequestData, PullRequestInfo} from './pr-collector/pullRequests'
|
||||||
import {BaseRepository} from './repositories/BaseRepository'
|
import {BaseRepository} from './repositories/BaseRepository'
|
||||||
|
|
||||||
export interface ReleaseNotesOptions {
|
export interface ReleaseNotesOptions {
|
||||||
|
|||||||
+368
-199
@@ -1,38 +1,34 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Category, Configuration, Placeholder, Property} from './configuration'
|
import {Category, Configuration, Placeholder, PlaceholderGroup, Property} from './configuration'
|
||||||
import {createOrSet, haveCommonElementsArr, haveEveryElementsArr} from './utils'
|
import {createOrSet, groupPlaceholders, haveCommonElementsArr, haveEveryElementsArr, mergeMaps} from './utils'
|
||||||
import {
|
import {
|
||||||
CommentInfo,
|
CommentInfo,
|
||||||
EMPTY_COMMENT_INFO,
|
EMPTY_COMMENT_INFO,
|
||||||
EMPTY_PULL_REQUEST_INFO,
|
EMPTY_PULL_REQUEST_INFO,
|
||||||
|
PullRequestData,
|
||||||
PullRequestInfo,
|
PullRequestInfo,
|
||||||
retrieveProperty,
|
retrieveProperty,
|
||||||
sortPullRequests
|
sortPullRequests
|
||||||
} from './pr-collector/pullRequests'
|
} from './pr-collector/pullRequests'
|
||||||
import {DiffInfo} from './pr-collector/commits'
|
import {DiffInfo} from './pr-collector/commits'
|
||||||
import {transformStringToOptionalValue, transformStringToValues, validateRegex} from './pr-collector/regexUtils'
|
import {transformStringToOptionalValue, transformStringToValues, validateRegex} from './pr-collector/regexUtils'
|
||||||
import {Regex, RegexTransformer} from './pr-collector/types'
|
import {ChangelogStrings, GroupedTemplateContext, PrStrings, Regex, RegexTransformer, TemplateContext} from './pr-collector/types'
|
||||||
import {ReleaseNotesOptions} from './releaseNotesBuilder'
|
import {ReleaseNotesOptions} from './releaseNotesBuilder'
|
||||||
import {matchesRules} from './regexUtils'
|
import {matchesRules} from './regexUtils'
|
||||||
|
|
||||||
const EMPTY_MAP = new Map<string, string>()
|
|
||||||
let CLEAR = false
|
let CLEAR = false
|
||||||
|
|
||||||
export function clear(): void {
|
export function clear(): void {
|
||||||
CLEAR = true
|
CLEAR = true
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PullRequestData extends PullRequestInfo {
|
|
||||||
childPrs?: PullRequestInfo[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], options: ReleaseNotesOptions): string {
|
export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], options: ReleaseNotesOptions): string {
|
||||||
core.startGroup('📦 Build changelog')
|
core.startGroup('📦 Build changelog')
|
||||||
|
|
||||||
let prs: PullRequestData[] = origPrs
|
let prs: PullRequestData[] = origPrs
|
||||||
if (prs.length === 0) {
|
if (prs.length === 0) {
|
||||||
core.warning(`⚠️ No pull requests found`)
|
core.warning(`⚠️ No pull requests found`)
|
||||||
const result = replaceEmptyTemplate(options.configuration.empty_template, options)
|
const result = renderEmptyChangelogTemplate(options.configuration.empty_template, options)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -123,31 +119,33 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep reference for the placeholder values
|
const groupedPlaceholders = groupPlaceholders(config.custom_placeholders || [])
|
||||||
const placeholders = new Map<string, Placeholder[]>()
|
|
||||||
for (const ph of config.custom_placeholders || []) {
|
const customPlaceholdersTemplateContext = new GroupedTemplateContext()
|
||||||
createOrSet(placeholders, ph.source, ph)
|
|
||||||
}
|
|
||||||
const placeholderPrMap = new Map<string, string[]>()
|
|
||||||
|
|
||||||
const validatedTransformers = validateTransformers(config.transformers)
|
const validatedTransformers = validateTransformers(config.transformers)
|
||||||
const transformedMap = new Map<PullRequestInfo, string>()
|
|
||||||
// convert PRs to their text representation
|
core.info(`ℹ️ Using ${validatedTransformers.length} transformers to rewrite content`)
|
||||||
for (const pr of prs) {
|
|
||||||
transformedMap.set(pr, transform(fillPrTemplate(pr, config.pr_template, placeholders, placeholderPrMap, config), validatedTransformers))
|
if (validatedTransformers.length > 0) {
|
||||||
|
for (const pr of prs) {
|
||||||
|
const prAsObject = pr as unknown as Record<string, unknown>
|
||||||
|
transformObject(prAsObject, validatedTransformers)
|
||||||
|
}
|
||||||
|
core.info(`✒️ Transformed ${prs.length} pull requests`)
|
||||||
}
|
}
|
||||||
core.info(`ℹ️ Used ${validatedTransformers.length} transformers to adjust message`)
|
|
||||||
core.info(`✒️ Wrote messages for ${prs.length} pull requests`)
|
const prInfoMap = buildInfoMapAndFillPlaceholderContext(
|
||||||
|
prs,
|
||||||
|
config.pr_template,
|
||||||
|
groupedPlaceholders,
|
||||||
|
customPlaceholdersTemplateContext,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
// bring PRs into the order of categories
|
// bring PRs into the order of categories
|
||||||
const categories = config.categories
|
const categories = config.categories
|
||||||
const ignoredLabels = config.ignore_labels
|
|
||||||
|
|
||||||
const flatCategories = flatten(config.categories)
|
const flatCategories = flatten(config.categories)
|
||||||
const categorizedPrs: string[] = []
|
|
||||||
const ignoredPrs: string[] = []
|
|
||||||
const openPrs: string[] = []
|
|
||||||
const uncategorizedPrs: string[] = []
|
|
||||||
|
|
||||||
// set-up the category object
|
// set-up the category object
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
@@ -156,8 +154,109 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prStrings = buildPrStringsAndFillCategoryEntries(prInfoMap, config.ignore_labels, categories, flatCategories)
|
||||||
|
core.info(`ℹ️ Ordered all pull requests into ${categories.length} categories`)
|
||||||
|
|
||||||
|
// serialize and provide the categorized content as json
|
||||||
|
const transformedCategorized = buildCategorizedOutput(flatCategories)
|
||||||
|
core.setOutput('categorized', JSON.stringify(transformedCategorized))
|
||||||
|
|
||||||
|
// construct final changelog
|
||||||
|
const changelogStrings = buildChangelogStrings(flatCategories, prStrings)
|
||||||
|
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.categorized.length} categorized pull requests down`)
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.uncategorized.length} non categorized pull requests down`)
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.open.length} open pull requests down`)
|
||||||
|
core.info(`✒️ Wrote ${changelogStrings.ignored.length} ignored pull requests down`)
|
||||||
|
|
||||||
|
core.setOutput('categorized_prs', changelogStrings.categorized.length)
|
||||||
|
core.setOutput('uncategorized_prs', changelogStrings.uncategorized.length)
|
||||||
|
core.setOutput('open_prs', changelogStrings.open.length)
|
||||||
|
core.setOutput('ignored_prs', changelogStrings.ignored.length)
|
||||||
|
|
||||||
|
// collect all contributors
|
||||||
|
const contributorsSet: Set<string> = new Set(prs.map(pr => `@${pr.author}`))
|
||||||
|
const contributorsArray = Array.from(contributorsSet)
|
||||||
|
const contributorsString = contributorsArray.join(', ')
|
||||||
|
const externalContributorString = contributorsArray.filter(value => value !== options.owner).join(', ')
|
||||||
|
core.setOutput('contributors', JSON.stringify(contributorsSet))
|
||||||
|
|
||||||
|
const releaseNotesTemplateContext = buildReleaseNotesTemplateContext(
|
||||||
|
changelogStrings,
|
||||||
|
contributorsString,
|
||||||
|
externalContributorString,
|
||||||
|
prStrings,
|
||||||
|
diffInfo,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
let renderedReleaseNotesTemplate = renderTemplateAndFillPlaceholderContext(
|
||||||
|
config.template,
|
||||||
|
releaseNotesTemplateContext,
|
||||||
|
groupedPlaceholders,
|
||||||
|
customPlaceholdersTemplateContext,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
renderedReleaseNotesTemplate = renderTemplateWithContext(renderedReleaseNotesTemplate, customPlaceholdersTemplateContext, config)
|
||||||
|
renderedReleaseNotesTemplate = cleanupPrPlaceholders(renderedReleaseNotesTemplate, groupedPlaceholders)
|
||||||
|
renderedReleaseNotesTemplate = cleanupPlaceholders(renderedReleaseNotesTemplate)
|
||||||
|
|
||||||
|
core.info(`ℹ️ Filled template`)
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
|
return renderedReleaseNotesTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildInfoMapAndFillPlaceholderContext(
|
||||||
|
prData: PullRequestData[],
|
||||||
|
template: string,
|
||||||
|
groupedPlaceholders: Map<string, Placeholder[]>,
|
||||||
|
customPlaceholdersTemplateContext: GroupedTemplateContext,
|
||||||
|
config: Configuration
|
||||||
|
): Map<PullRequestInfo, string> {
|
||||||
|
const infoMap = new Map<PullRequestInfo, string>()
|
||||||
|
|
||||||
|
for (const pr of prData) {
|
||||||
|
const [prTemplateContext, prArrayTemplateContext] = buildPrTemplateContext(pr)
|
||||||
|
|
||||||
|
let renderedTemplate = template
|
||||||
|
|
||||||
|
renderedTemplate = renderTemplateAndFillPlaceholderContext(
|
||||||
|
renderedTemplate,
|
||||||
|
prArrayTemplateContext,
|
||||||
|
groupedPlaceholders,
|
||||||
|
customPlaceholdersTemplateContext,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
renderedTemplate = renderTemplateAndFillPlaceholderContext(
|
||||||
|
renderedTemplate,
|
||||||
|
prTemplateContext,
|
||||||
|
groupedPlaceholders,
|
||||||
|
customPlaceholdersTemplateContext,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
infoMap.set(pr, renderedTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoMap
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrStringsAndFillCategoryEntries(
|
||||||
|
prInfoMap: Map<PullRequestInfo, string>,
|
||||||
|
ignoredLabels: string[],
|
||||||
|
categories: Category[],
|
||||||
|
flatCategories: Category[]
|
||||||
|
): PrStrings {
|
||||||
|
const categorizedPrs: string[] = []
|
||||||
|
const ignoredPrs: string[] = []
|
||||||
|
const openPrs: string[] = []
|
||||||
|
const uncategorizedPrs: string[] = []
|
||||||
|
|
||||||
// bring elements in order
|
// bring elements in order
|
||||||
prLoop: for (const [pr, body] of transformedMap) {
|
prLoop: for (const [pr, body] of prInfoMap) {
|
||||||
if (
|
if (
|
||||||
haveCommonElementsArr(
|
haveCommonElementsArr(
|
||||||
ignoredLabels.map(lbl => lbl.toLocaleLowerCase('en')),
|
ignoredLabels.map(lbl => lbl.toLocaleLowerCase('en')),
|
||||||
@@ -184,7 +283,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
if (!matchedOnce) {
|
if (!matchedOnce) {
|
||||||
// we allow to have pull requests included in an "uncategorized" category
|
// we allow to have pull requests included in an "uncategorized" category
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
const pullRequests = category.entries || []
|
category.entries = category.entries || []
|
||||||
if ((category.labels === undefined || category.labels.length === 0) && category.rules === undefined) {
|
if ((category.labels === undefined || category.labels.length === 0) && category.rules === undefined) {
|
||||||
// check if any exclude label matches for the "uncategorized" category
|
// check if any exclude label matches for the "uncategorized" category
|
||||||
if (category.exclude_labels !== undefined) {
|
if (category.exclude_labels !== undefined) {
|
||||||
@@ -194,7 +293,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
pr.labels
|
pr.labels
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
pullRequests.push(body)
|
category.entries.push(body)
|
||||||
} else if (core.isDebug()) {
|
} else if (core.isDebug()) {
|
||||||
const excludeLabels = JSON.stringify(category.exclude_labels)
|
const excludeLabels = JSON.stringify(category.exclude_labels)
|
||||||
core.debug(
|
core.debug(
|
||||||
@@ -202,7 +301,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pullRequests.push(body)
|
category.entries.push(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -215,106 +314,126 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
categorizedPrs.push(body)
|
categorizedPrs.push(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.info(`ℹ️ Ordered all pull requests into ${categories.length} categories`)
|
|
||||||
|
|
||||||
// serialize and provide the categorized content as json
|
const prStrings: PrStrings = {
|
||||||
const transformedCategorized = {}
|
categorizedList: categorizedPrs,
|
||||||
for (const category of flatCategories) {
|
uncategorizedList: uncategorizedPrs,
|
||||||
Object.assign(transformedCategorized, {[category.key || category.title]: category.entries})
|
openList: openPrs,
|
||||||
|
ignoredList: ignoredPrs
|
||||||
}
|
}
|
||||||
core.setOutput('categorized', JSON.stringify(transformedCategorized))
|
|
||||||
|
|
||||||
// construct final changelog
|
return prStrings
|
||||||
let changelog = ''
|
}
|
||||||
|
|
||||||
|
function buildChangelogStrings(flatCategories: Category[], prStrings: PrStrings): ChangelogStrings {
|
||||||
|
const {categorizedList, uncategorizedList, openList, ignoredList} = prStrings
|
||||||
|
|
||||||
|
let changelogCategorized = ''
|
||||||
for (const category of flatCategories) {
|
for (const category of flatCategories) {
|
||||||
const pullRequests = category.entries || []
|
const pullRequests = category.entries || []
|
||||||
changelog = attachCategoryChangelog(changelog, category, pullRequests)
|
changelogCategorized += buildCategorizedChangelogString(category, pullRequests)
|
||||||
}
|
}
|
||||||
core.info(`✒️ Wrote ${categorizedPrs.length} categorized pull requests down`)
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
for (const pr of categorizedPrs) {
|
for (const pr of categorizedList) {
|
||||||
core.debug(` ${pr}`)
|
core.debug(` ${pr}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.setOutput('categorized_prs', categorizedPrs.length)
|
|
||||||
|
|
||||||
let changelogUncategorized = ''
|
let changelogUncategorized = ''
|
||||||
for (const pr of uncategorizedPrs) {
|
for (const pr of uncategorizedList) {
|
||||||
changelogUncategorized = `${changelogUncategorized + pr}\n`
|
changelogUncategorized = `${changelogUncategorized + pr}\n`
|
||||||
}
|
}
|
||||||
core.info(`✒️ Wrote ${uncategorizedPrs.length} non categorized pull requests down`)
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
for (const pr of uncategorizedPrs) {
|
for (const pr of uncategorizedList) {
|
||||||
core.debug(` ${pr}`)
|
core.debug(` ${pr}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.setOutput('uncategorized_prs', uncategorizedPrs.length)
|
|
||||||
|
|
||||||
let changelogOpen = ''
|
let changelogOpen = ''
|
||||||
if (openPrs.length > 0) {
|
if (openList.length > 0) {
|
||||||
for (const pr of openPrs) {
|
for (const pr of openList) {
|
||||||
changelogOpen = `${changelogOpen + pr}\n`
|
changelogOpen = `${changelogOpen + pr}\n`
|
||||||
}
|
}
|
||||||
core.info(`✒️ Wrote ${openPrs.length} open pull requests down`)
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
for (const pr of openPrs) {
|
for (const pr of openList) {
|
||||||
core.debug(` ${pr}`)
|
core.debug(` ${pr}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.setOutput('open_prs', openPrs.length)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let changelogIgnored = ''
|
let changelogIgnored = ''
|
||||||
for (const pr of ignoredPrs) {
|
for (const pr of ignoredList) {
|
||||||
changelogIgnored = `${changelogIgnored + pr}\n`
|
changelogIgnored = `${changelogIgnored + pr}\n`
|
||||||
}
|
}
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
for (const pr of ignoredPrs) {
|
for (const pr of ignoredList) {
|
||||||
core.debug(` ${pr}`)
|
core.debug(` ${pr}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.info(`✒️ Wrote ${ignoredPrs.length} ignored pull requests down`)
|
|
||||||
|
|
||||||
// collect all contributors
|
const changelogStrings: ChangelogStrings = {
|
||||||
const contributorsSet: Set<String> = new Set()
|
categorized: changelogCategorized,
|
||||||
for (const pr of prs) {
|
uncategorized: changelogUncategorized,
|
||||||
contributorsSet.add(`@${pr.author}`)
|
open: changelogOpen,
|
||||||
|
ignored: changelogIgnored
|
||||||
}
|
}
|
||||||
const contributorsArray = Array.from(contributorsSet)
|
|
||||||
const contributorsString = contributorsArray.join(', ')
|
|
||||||
const externalContributorString = contributorsArray.filter(value => value !== options.owner).join(', ')
|
|
||||||
core.setOutput('contributors', JSON.stringify(contributorsSet))
|
|
||||||
|
|
||||||
// fill template
|
return changelogStrings
|
||||||
const placeholderMap = new Map<string, string>()
|
}
|
||||||
placeholderMap.set('CHANGELOG', changelog)
|
|
||||||
placeholderMap.set('UNCATEGORIZED', changelogUncategorized)
|
function buildReleaseNotesTemplateContext(
|
||||||
placeholderMap.set('OPEN', changelogOpen)
|
changelogStrings: ChangelogStrings,
|
||||||
placeholderMap.set('IGNORED', changelogIgnored)
|
contributorsString: string,
|
||||||
|
externalContributorString: string,
|
||||||
|
prStrings: PrStrings,
|
||||||
|
diffInfo: DiffInfo,
|
||||||
|
options: ReleaseNotesOptions
|
||||||
|
): TemplateContext {
|
||||||
|
const {
|
||||||
|
categorized: changelogCategorized,
|
||||||
|
uncategorized: changelogUncategorized,
|
||||||
|
open: changelogOpen,
|
||||||
|
ignored: changelogIgnored
|
||||||
|
} = changelogStrings
|
||||||
|
|
||||||
|
const {categorizedList, uncategorizedList, openList, ignoredList} = prStrings
|
||||||
|
|
||||||
|
let releaseNotesTemplateContext = new TemplateContext()
|
||||||
|
|
||||||
|
releaseNotesTemplateContext.set('CHANGELOG', changelogCategorized)
|
||||||
|
releaseNotesTemplateContext.set('UNCATEGORIZED', changelogUncategorized)
|
||||||
|
releaseNotesTemplateContext.set('OPEN', changelogOpen)
|
||||||
|
releaseNotesTemplateContext.set('IGNORED', changelogIgnored)
|
||||||
// fill special collected contributors
|
// fill special collected contributors
|
||||||
placeholderMap.set('CONTRIBUTORS', contributorsString)
|
releaseNotesTemplateContext.set('CONTRIBUTORS', contributorsString)
|
||||||
placeholderMap.set('EXTERNAL_CONTRIBUTORS', externalContributorString)
|
releaseNotesTemplateContext.set('EXTERNAL_CONTRIBUTORS', externalContributorString)
|
||||||
// fill other placeholders
|
// fill other placeholders
|
||||||
placeholderMap.set('CATEGORIZED_COUNT', categorizedPrs.length.toString())
|
releaseNotesTemplateContext.set('CATEGORIZED_COUNT', categorizedList.length.toString())
|
||||||
placeholderMap.set('UNCATEGORIZED_COUNT', uncategorizedPrs.length.toString())
|
releaseNotesTemplateContext.set('UNCATEGORIZED_COUNT', uncategorizedList.length.toString())
|
||||||
placeholderMap.set('OPEN_COUNT', openPrs.length.toString())
|
releaseNotesTemplateContext.set('OPEN_COUNT', openList.length.toString())
|
||||||
placeholderMap.set('IGNORED_COUNT', ignoredPrs.length.toString())
|
releaseNotesTemplateContext.set('IGNORED_COUNT', ignoredList.length.toString())
|
||||||
// code change placeholders
|
// code change placeholders
|
||||||
placeholderMap.set('CHANGED_FILES', diffInfo.changedFiles.toString())
|
releaseNotesTemplateContext.set('CHANGED_FILES', diffInfo.changedFiles.toString())
|
||||||
placeholderMap.set('ADDITIONS', diffInfo.additions.toString())
|
releaseNotesTemplateContext.set('ADDITIONS', diffInfo.additions.toString())
|
||||||
placeholderMap.set('DELETIONS', diffInfo.deletions.toString())
|
releaseNotesTemplateContext.set('DELETIONS', diffInfo.deletions.toString())
|
||||||
placeholderMap.set('CHANGES', diffInfo.changes.toString())
|
releaseNotesTemplateContext.set('CHANGES', diffInfo.changes.toString())
|
||||||
placeholderMap.set('COMMITS', diffInfo.commits.toString())
|
releaseNotesTemplateContext.set('COMMITS', diffInfo.commits.toString())
|
||||||
fillAdditionalPlaceholders(options, placeholderMap)
|
|
||||||
|
|
||||||
let transformedChangelog = config.template
|
const coreReleasesNotesContext = buildCoreReleaseNotesTemplateContext(options)
|
||||||
transformedChangelog = replacePlaceholders(transformedChangelog, EMPTY_MAP, placeholderMap, placeholders, placeholderPrMap, config)
|
|
||||||
transformedChangelog = replacePrPlaceholders(transformedChangelog, placeholderPrMap, config)
|
releaseNotesTemplateContext = mergeMaps(releaseNotesTemplateContext, coreReleasesNotesContext)
|
||||||
transformedChangelog = cleanupPrPlaceholders(transformedChangelog, placeholders)
|
|
||||||
transformedChangelog = cleanupPlaceholders(transformedChangelog)
|
return releaseNotesTemplateContext
|
||||||
core.info(`ℹ️ Filled template`)
|
}
|
||||||
core.endGroup()
|
|
||||||
return transformedChangelog
|
function buildCategorizedOutput(flatCategories: Category[]): Record<string, string[]> {
|
||||||
|
const transformedCategorized = {}
|
||||||
|
|
||||||
|
for (const category of flatCategories) {
|
||||||
|
Object.assign(transformedCategorized, {[category.key || category.title]: category.entries})
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformedCategorized
|
||||||
}
|
}
|
||||||
|
|
||||||
function recursiveCategorizePr(category: Category, pr: PullRequestInfo, body: string): boolean[] {
|
function recursiveCategorizePr(category: Category, pr: PullRequestInfo, body: string): boolean[] {
|
||||||
@@ -334,10 +453,11 @@ function recursiveCategorizePr(category: Category, pr: PullRequestInfo, body: st
|
|||||||
|
|
||||||
// if consumed we don't handle it anymore, as it was matched in a child, don't handle anymore
|
// if consumed we don't handle it anymore, as it was matched in a child, don't handle anymore
|
||||||
if (!consumed && !matched) {
|
if (!consumed && !matched) {
|
||||||
const pullRequests = category.entries || []
|
category.entries = category.entries || []
|
||||||
|
|
||||||
matched = matchesParent
|
matched = matchesParent
|
||||||
if (matched) {
|
if (matched) {
|
||||||
pullRequests.push(body) // if matched add the PR to the list
|
category.entries.push(body) // if matched add the PR to the list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (matched && category.consume) {
|
if (matched && category.consume) {
|
||||||
@@ -401,156 +521,185 @@ function categorizePr(category: Category, pr: PullRequestInfo): boolean {
|
|||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachCategoryChangelog(changelog: string, category: Category, pullRequests: string[]): string {
|
function buildCategorizedChangelogString(category: Category, pullRequests: string[]): string {
|
||||||
|
let categorizedString = ''
|
||||||
|
|
||||||
if (pullRequests.length > 0 || hasChildWithEntries(category)) {
|
if (pullRequests.length > 0 || hasChildWithEntries(category)) {
|
||||||
if (category.title) {
|
if (category.title) {
|
||||||
changelog = `${changelog + category.title}\n\n`
|
categorizedString = `${categorizedString + category.title}\n\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pr of pullRequests) {
|
for (const pr of pullRequests) {
|
||||||
changelog = `${changelog + pr}\n`
|
categorizedString = `${categorizedString + pr}\n`
|
||||||
}
|
}
|
||||||
changelog = `${changelog}\n` // add space between sections
|
categorizedString = `${categorizedString}\n` // add space between sections
|
||||||
} else if (category.empty_content !== undefined) {
|
} else if (category.empty_content !== undefined) {
|
||||||
if (category.title) {
|
if (category.title) {
|
||||||
changelog = `${changelog + category.title}\n\n`
|
categorizedString = `${categorizedString + category.title}\n\n`
|
||||||
}
|
}
|
||||||
changelog = `${changelog + category.empty_content}\n\n`
|
categorizedString = `${categorizedString + category.empty_content}\n\n`
|
||||||
}
|
}
|
||||||
return changelog
|
return categorizedString
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceEmptyTemplate(template: string, options: ReleaseNotesOptions): string {
|
export function renderEmptyChangelogTemplate(template: string, options: ReleaseNotesOptions): string {
|
||||||
const placeholders = new Map<string, Placeholder[]>()
|
const placeholders = new Map<string, Placeholder[]>()
|
||||||
for (const ph of options.configuration.custom_placeholders || []) {
|
for (const ph of options.configuration.custom_placeholders || []) {
|
||||||
createOrSet(placeholders, ph.source, ph)
|
createOrSet(placeholders, ph.source, ph)
|
||||||
}
|
}
|
||||||
const placeholderMap = new Map<string, string>()
|
|
||||||
fillAdditionalPlaceholders(options, placeholderMap)
|
const releaseNotesTemplateContext = buildCoreReleaseNotesTemplateContext(options)
|
||||||
return replacePlaceholders(template, EMPTY_MAP, placeholderMap, placeholders, undefined, options.configuration)
|
|
||||||
|
const renderedEmptyChangelogTemplate = renderTemplateAndFillPlaceholderContext(
|
||||||
|
template,
|
||||||
|
releaseNotesTemplateContext,
|
||||||
|
placeholders,
|
||||||
|
undefined,
|
||||||
|
options.configuration
|
||||||
|
)
|
||||||
|
|
||||||
|
return renderedEmptyChangelogTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillAdditionalPlaceholders(
|
function buildCoreReleaseNotesTemplateContext(options: ReleaseNotesOptions): TemplateContext {
|
||||||
options: ReleaseNotesOptions,
|
const templateContext = new TemplateContext()
|
||||||
placeholderMap: Map<string, string> /* placeholderKey and original value */
|
|
||||||
): void {
|
templateContext.set('OWNER', options.owner)
|
||||||
placeholderMap.set('OWNER', options.owner)
|
templateContext.set('REPO', options.repo)
|
||||||
placeholderMap.set('REPO', options.repo)
|
templateContext.set('FROM_TAG', options.fromTag.name)
|
||||||
placeholderMap.set('FROM_TAG', options.fromTag.name)
|
templateContext.set('FROM_TAG_DATE', options.fromTag.date?.toISOString() || '')
|
||||||
placeholderMap.set('FROM_TAG_DATE', options.fromTag.date?.toISOString() || '')
|
templateContext.set('TO_TAG', options.toTag.name)
|
||||||
placeholderMap.set('TO_TAG', options.toTag.name)
|
templateContext.set('TO_TAG_DATE', options.toTag.date?.toISOString() || '')
|
||||||
placeholderMap.set('TO_TAG_DATE', options.toTag.date?.toISOString() || '')
|
|
||||||
const fromDate = options.fromTag.date
|
const fromDate = options.fromTag.date
|
||||||
const toDate = options.toTag.date
|
const toDate = options.toTag.date
|
||||||
if (fromDate !== undefined && toDate !== undefined) {
|
if (fromDate !== undefined && toDate !== undefined) {
|
||||||
placeholderMap.set('DAYS_SINCE', toDate.diff(fromDate, 'days').toString() || '')
|
templateContext.set('DAYS_SINCE', toDate.diff(fromDate, 'days').toString() || '')
|
||||||
} else {
|
} else {
|
||||||
placeholderMap.set('DAYS_SINCE', '')
|
templateContext.set('DAYS_SINCE', '')
|
||||||
}
|
}
|
||||||
placeholderMap.set(
|
templateContext.set(
|
||||||
'RELEASE_DIFF',
|
'RELEASE_DIFF',
|
||||||
`${options.repositoryUtils.homeUrl}/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`
|
`${options.repositoryUtils.homeUrl}/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return templateContext
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillPrTemplate(
|
function buildPrTemplateContext(pr: PullRequestData): [TemplateContext, TemplateContext] {
|
||||||
pr: PullRequestData,
|
const prTemplateContext = new TemplateContext()
|
||||||
template: string,
|
|
||||||
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
|
prTemplateContext.set('NUMBER', pr.number.toString())
|
||||||
placeholderPrMap: Map<string, string[]> /* map to keep replaced placeholder values with their key */,
|
prTemplateContext.set('TITLE', pr.title)
|
||||||
configuration: Configuration
|
prTemplateContext.set('URL', pr.htmlURL)
|
||||||
): string {
|
prTemplateContext.set('STATUS', pr.status)
|
||||||
const arrayPlaceholderMap = new Map<string, string>()
|
prTemplateContext.set('CREATED_AT', pr.createdAt.toISOString())
|
||||||
fillReviewPlaceholders(arrayPlaceholderMap, 'REVIEWS', pr.reviews || [])
|
prTemplateContext.set('MERGED_AT', pr.mergedAt?.toISOString() || '')
|
||||||
fillChildPrPlaceholders(arrayPlaceholderMap, 'REFERENCED', pr.childPrs || [])
|
prTemplateContext.set('MERGE_SHA', pr.mergeCommitSha)
|
||||||
const placeholderMap = new Map<string, string>()
|
prTemplateContext.set('AUTHOR', pr.author)
|
||||||
placeholderMap.set('NUMBER', pr.number.toString())
|
prTemplateContext.set('AUTHOR_NAME', pr.authorName || '')
|
||||||
placeholderMap.set('TITLE', pr.title)
|
prTemplateContext.set('LABELS', [...pr.labels]?.filter(l => !l.startsWith('--rcba-'))?.join(', ') || '')
|
||||||
placeholderMap.set('URL', pr.htmlURL)
|
prTemplateContext.set('MILESTONE', pr.milestone || '')
|
||||||
placeholderMap.set('STATUS', pr.status)
|
prTemplateContext.set('BODY', pr.body)
|
||||||
placeholderMap.set('CREATED_AT', pr.createdAt.toISOString())
|
prTemplateContext.set('ASSIGNEES', pr.assignees?.join(', ') || '')
|
||||||
placeholderMap.set('MERGED_AT', pr.mergedAt?.toISOString() || '')
|
prTemplateContext.set('REVIEWERS', pr.requestedReviewers?.join(', ') || '')
|
||||||
placeholderMap.set('MERGE_SHA', pr.mergeCommitSha)
|
prTemplateContext.set('APPROVERS', pr.approvedReviewers?.join(', ') || '')
|
||||||
placeholderMap.set('AUTHOR', pr.author)
|
prTemplateContext.set('BRANCH', pr.branch || '')
|
||||||
placeholderMap.set('AUTHOR_NAME', pr.authorName || '')
|
prTemplateContext.set('BASE_BRANCH', pr.baseBranch)
|
||||||
placeholderMap.set('LABELS', [...pr.labels]?.filter(l => !l.startsWith('--rcba-'))?.join(', ') || '')
|
|
||||||
placeholderMap.set('MILESTONE', pr.milestone || '')
|
const prArrayTemplateContext = new TemplateContext()
|
||||||
placeholderMap.set('BODY', pr.body)
|
fillReviewPlaceholders(prArrayTemplateContext, 'REVIEWS', pr.reviews || [])
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'ASSIGNEES', pr.assignees || [])
|
fillChildPrPlaceholders(prArrayTemplateContext, 'REFERENCED', pr.childPrs || [])
|
||||||
placeholderMap.set('ASSIGNEES', pr.assignees?.join(', ') || '')
|
fillArrayPlaceholders(prArrayTemplateContext, 'ASSIGNEES', pr.assignees || [])
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'REVIEWERS', pr.requestedReviewers || [])
|
fillArrayPlaceholders(prArrayTemplateContext, 'REVIEWERS', pr.requestedReviewers || [])
|
||||||
placeholderMap.set('REVIEWERS', pr.requestedReviewers?.join(', ') || '')
|
fillArrayPlaceholders(prArrayTemplateContext, 'APPROVERS', pr.approvedReviewers || [])
|
||||||
fillArrayPlaceholders(arrayPlaceholderMap, 'APPROVERS', pr.approvedReviewers || [])
|
|
||||||
placeholderMap.set('APPROVERS', pr.approvedReviewers?.join(', ') || '')
|
return [prTemplateContext, prArrayTemplateContext]
|
||||||
placeholderMap.set('BRANCH', pr.branch || '')
|
|
||||||
placeholderMap.set('BASE_BRANCH', pr.baseBranch)
|
|
||||||
return replacePlaceholders(template, arrayPlaceholderMap, placeholderMap, placeholders, placeholderPrMap, configuration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replacePlaceholders(
|
function renderTemplateAndFillPlaceholderContext(
|
||||||
template: string,
|
template: string,
|
||||||
arrayPlaceholderMap: Map<string, string> /* arrayPlaceholderKey and original value */,
|
templateContext: TemplateContext /* placeholderKey and original value */,
|
||||||
placeholderMap: Map<string, string> /* placeholderKey and original value */,
|
customPlaceholders: PlaceholderGroup /* placeholders to apply */,
|
||||||
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
|
customPlaceholdersTemplateContext: GroupedTemplateContext | undefined /* map to keep replaced placeholder values with their key */,
|
||||||
placeholderPrMap: Map<string, string[]> | undefined /* map to keep replaced placeholder values with their key */,
|
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
): string {
|
): string {
|
||||||
let transformed = template
|
let transformed = template
|
||||||
|
|
||||||
// replace array placeholders first
|
const trimValues = configuration.trim_values
|
||||||
for (const [key, value] of arrayPlaceholderMap) {
|
|
||||||
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap, configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace traditional placeholders
|
// replace traditional placeholders
|
||||||
for (const [key, value] of placeholderMap) {
|
for (const [key, value] of templateContext) {
|
||||||
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap, configuration)
|
transformed = transformed.replaceAll(`#{{${key}}}`, trimValues ? value.trim() : value)
|
||||||
|
|
||||||
|
const extractedValues = extractPlaceholderValuesAndFillPlaceholderContext(
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
customPlaceholders,
|
||||||
|
customPlaceholdersTemplateContext
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const [placeholderName, extractedValue] of extractedValues) {
|
||||||
|
transformed = transformed.replaceAll(`#{{${placeholderName}}}`, trimValues ? extractedValue.trim() : extractedValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformed
|
return transformed
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePlaceholder(
|
function extractPlaceholderValuesAndFillPlaceholderContext(
|
||||||
template: string,
|
|
||||||
key: string,
|
key: string,
|
||||||
value: string,
|
value: string,
|
||||||
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
|
customPlaceholders: PlaceholderGroup,
|
||||||
placeholderPrMap: Map<string, string[]> | undefined /* map to keep replaced placeholder values with their key */,
|
customPlaceholdersTemplateContext: GroupedTemplateContext | undefined
|
||||||
configuration: Configuration
|
): TemplateContext {
|
||||||
): string {
|
// Replace custom placeholders
|
||||||
let transformed = template.replaceAll(`#{{${key}}}`, configuration.trim_values ? value.trim() : value)
|
const placeholdersForKey = customPlaceholders.get(key)
|
||||||
// replace custom placeholders
|
|
||||||
const phs = placeholders.get(key)
|
|
||||||
if (phs) {
|
|
||||||
for (const placeholder of phs) {
|
|
||||||
const transformer = validateRegex(placeholder.transformer)
|
|
||||||
if (transformer?.pattern) {
|
|
||||||
const extractedValue = transformStringToOptionalValue(value, transformer)
|
|
||||||
// note: `.replace` will return the full string again if there was no match
|
|
||||||
// note: This is mostly backwards compatibility
|
|
||||||
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
|
|
||||||
if (placeholderPrMap) {
|
|
||||||
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
|
|
||||||
}
|
|
||||||
transformed = transformed.replaceAll(
|
|
||||||
`#{{${placeholder.name}}}`,
|
|
||||||
configuration.trim_values ? extractedValue.trim() : extractedValue
|
|
||||||
)
|
|
||||||
|
|
||||||
if (core.isDebug()) {
|
const extractedValues = new TemplateContext()
|
||||||
core.debug(` Custom Placeholder successfully matched data - ${extractedValue} (${placeholder.name})`)
|
|
||||||
}
|
if (!placeholdersForKey) {
|
||||||
} else if (core.isDebug() && extractedValue === value) {
|
return extractedValues
|
||||||
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`)
|
}
|
||||||
}
|
|
||||||
|
for (const placeholder of placeholdersForKey) {
|
||||||
|
const extractedValue = extractTransformedValue(value, placeholder)
|
||||||
|
|
||||||
|
if (extractedValue) {
|
||||||
|
extractedValues.set(placeholder.name, extractedValue)
|
||||||
|
|
||||||
|
if (customPlaceholdersTemplateContext) {
|
||||||
|
createOrSet(customPlaceholdersTemplateContext, placeholder.name, extractedValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return transformed
|
|
||||||
|
return extractedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTransformedValue(value: string, placeholder: Placeholder): string | undefined {
|
||||||
|
const transformer = validateRegex(placeholder.transformer)
|
||||||
|
|
||||||
|
if (!transformer?.pattern) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractedValue = transformStringToOptionalValue(value, transformer)
|
||||||
|
|
||||||
|
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
|
||||||
|
if (core.isDebug()) {
|
||||||
|
core.debug(` Custom Placeholder successfully matched data - ${extractedValue} (${placeholder.name})`)
|
||||||
|
}
|
||||||
|
return extractedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (core.isDebug() && extractedValue === value) {
|
||||||
|
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillArrayPlaceholders(
|
function fillArrayPlaceholders(
|
||||||
placeholderMap: Map<string, string> /* placeholderKey and original value */,
|
placeholderMap: TemplateContext /* placeholderKey and original value */,
|
||||||
key: string,
|
key: string,
|
||||||
values: string[]
|
values: string[]
|
||||||
): void {
|
): void {
|
||||||
@@ -562,7 +711,7 @@ function fillArrayPlaceholders(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fillReviewPlaceholders(
|
function fillReviewPlaceholders(
|
||||||
placeholderMap: Map<string, string> /* placeholderKey and original value */,
|
placeholderMap: TemplateContext /* placeholderKey and original value */,
|
||||||
parentKey: string,
|
parentKey: string,
|
||||||
values: CommentInfo[]
|
values: CommentInfo[]
|
||||||
): void {
|
): void {
|
||||||
@@ -580,7 +729,7 @@ function fillReviewPlaceholders(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fillChildPrPlaceholders(
|
function fillChildPrPlaceholders(
|
||||||
placeholderMap: Map<string, string> /* placeholderKey and original value */,
|
placeholderMap: TemplateContext /* placeholderKey and original value */,
|
||||||
parentKey: string,
|
parentKey: string,
|
||||||
values: PullRequestInfo[]
|
values: PullRequestInfo[]
|
||||||
): void {
|
): void {
|
||||||
@@ -597,13 +746,13 @@ function fillChildPrPlaceholders(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function replacePrPlaceholders(
|
function renderTemplateWithContext(
|
||||||
template: string,
|
template: string,
|
||||||
placeholderPrMap: Map<string, string[]> /* map with all pr related custom placeholder values */,
|
templateContext: GroupedTemplateContext /* map with all pr related custom placeholder values */,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
): string {
|
): string {
|
||||||
let transformed = template
|
let transformed = template
|
||||||
for (const [key, values] of placeholderPrMap) {
|
for (const [key, values] of templateContext) {
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
transformed = transformed.replaceAll(`#{{${key}[${i}]}}`, configuration.trim_values ? values[i].trim() : values[i])
|
transformed = transformed.replaceAll(`#{{${key}[${i}]}}`, configuration.trim_values ? values[i].trim() : values[i])
|
||||||
}
|
}
|
||||||
@@ -612,7 +761,7 @@ function replacePrPlaceholders(
|
|||||||
return transformed
|
return transformed
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupPrPlaceholders(template: string, placeholders: Map<string, Placeholder[]>): string {
|
function cleanupPrPlaceholders(template: string, placeholders: PlaceholderGroup): string {
|
||||||
let transformed = template
|
let transformed = template
|
||||||
for (const [, phs] of placeholders) {
|
for (const [, phs] of placeholders) {
|
||||||
for (const ph of phs) {
|
for (const ph of phs) {
|
||||||
@@ -630,6 +779,27 @@ function cleanupPlaceholders(template: string): string {
|
|||||||
return transformed
|
return transformed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transformObject(obj: Record<string, unknown>, transformers: RegexTransformer[]): void {
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
if (value === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
if (value.every(item => typeof item === 'string')) {
|
||||||
|
// If the array contains only strings, apply the transformation to each string
|
||||||
|
obj[key] = value.map(item => transform(item, transformers))
|
||||||
|
} else {
|
||||||
|
// If the array contains objects, recursively apply the transformation to each object
|
||||||
|
for (const child of value) {
|
||||||
|
transformObject(child as Record<string, unknown>, transformers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
obj[key] = transform(value, transformers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function transform(filled: string, transformers: RegexTransformer[]): string {
|
function transform(filled: string, transformers: RegexTransformer[]): string {
|
||||||
if (transformers.length === 0) {
|
if (transformers.length === 0) {
|
||||||
return filled
|
return filled
|
||||||
@@ -644,8 +814,7 @@ function transform(filled: string, transformers: RegexTransformer[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateTransformers(specifiedTransformers: Regex[]): RegexTransformer[] {
|
function validateTransformers(specifiedTransformers: Regex[]): RegexTransformer[] {
|
||||||
const transformers = specifiedTransformers
|
return specifiedTransformers
|
||||||
return transformers
|
|
||||||
.map(transformer => {
|
.map(transformer => {
|
||||||
return validateRegex(transformer)
|
return validateRegex(transformer)
|
||||||
})
|
})
|
||||||
|
|||||||
+27
-2
@@ -1,7 +1,7 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {Configuration, DefaultCommitConfiguration, DefaultConfiguration} from './configuration'
|
import {Configuration, DefaultCommitConfiguration, DefaultConfiguration, Placeholder} from './configuration'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import {DiffInfo} from './pr-collector/commits'
|
import {DiffInfo} from './pr-collector/commits'
|
||||||
import {PullRequestInfo} from './pr-collector/pullRequests'
|
import {PullRequestInfo} from './pr-collector/pullRequests'
|
||||||
@@ -123,7 +123,7 @@ export function resolveMode(mode: string | undefined, commitMode: boolean): 'PR'
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode !== undefined) {
|
if (mode !== undefined) {
|
||||||
const upperCaseMode = mode.toUpperCase();
|
const upperCaseMode = mode.toUpperCase()
|
||||||
if (upperCaseMode === 'COMMIT') {
|
if (upperCaseMode === 'COMMIT') {
|
||||||
return 'COMMIT'
|
return 'COMMIT'
|
||||||
} else if (upperCaseMode === 'HYBRID') {
|
} else if (upperCaseMode === 'HYBRID') {
|
||||||
@@ -243,6 +243,27 @@ export function createOrSet<T>(map: Map<string, T[]>, key: string, value: T): vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups an array of Placeholder objects by their source attribute.
|
||||||
|
*
|
||||||
|
* @param {Placeholder[]} placeholders - An array of Placeholder objects to be grouped.
|
||||||
|
* @returns {Map<string, Placeholder[]>} A map where the key is the source attribute of the Placeholder objects,
|
||||||
|
* and the value is an array of Placeholder objects that share the same source attribute.
|
||||||
|
*/
|
||||||
|
export function groupPlaceholders(placeholders: Placeholder[]): Map<string, Placeholder[]> {
|
||||||
|
const map = new Map<string, Placeholder[]>()
|
||||||
|
for (const ph of placeholders) {
|
||||||
|
const key = ph.source
|
||||||
|
const entry = map.get(key)
|
||||||
|
if (entry === undefined) {
|
||||||
|
map.set(key, [ph])
|
||||||
|
} else {
|
||||||
|
entry.push(ph)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
export function haveCommonElements(arr1: string[], arr2: Set<string>): boolean {
|
export function haveCommonElements(arr1: string[], arr2: Set<string>): boolean {
|
||||||
return arr1.some(item => arr2.has(item))
|
return arr1.some(item => arr2.has(item))
|
||||||
}
|
}
|
||||||
@@ -258,3 +279,7 @@ export function haveEveryElements(arr1: string[], arr2: Set<string>): boolean {
|
|||||||
export function haveEveryElementsArr(arr1: string[], arr2: string[]): boolean {
|
export function haveEveryElementsArr(arr1: string[], arr2: string[]): boolean {
|
||||||
return haveEveryElements(arr1, new Set(arr2))
|
return haveEveryElements(arr1, new Set(arr2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeMaps<T, U>(map1: Map<T, U>, map2: Map<T, U>): Map<T, U> {
|
||||||
|
return new Map<T, U>([...map1, ...map2])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user