Files
release-changelog-builder-a…/src/transform.ts
T

154 lines
3.9 KiB
TypeScript

import {PullRequestInfo, sortPullRequests} from './pullRequests'
import * as core from '@actions/core'
import {
Category,
Configuration,
Transformer,
DefaultConfiguration
} from './configuration'
export function buildChangelog(
prs: PullRequestInfo[],
config: Configuration
): string {
// sort to target order
prs = sortPullRequests(
prs,
(config.sort ? config.sort : DefaultConfiguration.sort).toUpperCase() ===
'ASC'
)
const validatedTransformers = validateTransfomers(config.transformers)
const transformedMap = new Map<PullRequestInfo, string>()
// convert PRs to their text representation
for (const pr of prs) {
transformedMap.set(
pr,
transform(
fillTemplate(
pr,
config.pr_template
? config.pr_template
: DefaultConfiguration.pr_template
),
validatedTransformers
)
)
}
// bring PRs into the order of categories
const categorized = new Map<Category, string[]>()
if (config.categories) {
for (const category of config.categories) {
categorized.set(category, [])
}
}
const uncategorized: string[] = []
// bring elements in order
for (const [pr, body] of transformedMap) {
let matched = false
for (const [category, pullRequests] of categorized) {
if (haveCommonElements(category.labels, pr.labels)) {
pullRequests.push(body)
matched = true
}
}
if (!matched) {
uncategorized.push(body)
}
}
// construct final changelog
let changelog = ''
for (const [category, pullRequests] of categorized) {
if (pullRequests.length > 0) {
changelog = `${changelog + category.title}\n\n`
for (const pr of pullRequests) {
changelog = `${changelog + pr}\n`
}
// add space between
changelog = `${changelog}\n`
}
}
let changelogUncategorized = ''
for (const pr of uncategorized) {
changelogUncategorized = `${changelogUncategorized + pr}\n`
}
// fill template
let transformedChangelog = config.template
? config.template
: DefaultConfiguration.template
transformedChangelog = transformedChangelog.replace(
'${{CHANGELOG}}',
changelog
)
transformedChangelog = transformedChangelog.replace(
'${{UNCATEGORIZED}}',
changelogUncategorized
)
return transformedChangelog
}
function haveCommonElements(arr1: string[], arr2: string[]): Boolean {
return arr1.some(item => arr2.includes(item))
}
function fillTemplate(pr: PullRequestInfo, template: string): string {
let transformed = template
transformed = transformed.replace('${{NUMBER}}', pr.number.toString())
transformed = transformed.replace('${{TITLE}}', pr.title)
transformed = transformed.replace('${{URL}}', pr.htmlURL)
transformed = transformed.replace('${{MERGED_AT}}', pr.mergedAt.toISOString())
transformed = transformed.replace('${{AUTHOR}}', pr.author)
transformed = transformed.replace('${{BODY}}', pr.body)
return transformed
}
function transform(filled: string, transformers: RegexTransformer[]): string {
if (transformers.length === 0) {
return filled
}
let transformed = filled
for (const {target, pattern} of transformers) {
transformed = transformed.replace(pattern!!, target)
}
return transformed
}
function validateTransfomers(
specifiedTransformers: Transformer[]
): RegexTransformer[] {
const transformers = specifiedTransformers
? specifiedTransformers
: DefaultConfiguration.transformers
return transformers
.map(transformer => {
try {
return {
pattern: new RegExp(transformer.pattern.replace('\\\\', '\\'), 'g'),
target: transformer.target
}
} catch (e) {
core.warning(`Bad replacer regex: ${transformer.pattern}`)
return {
pattern: null,
target: ''
}
}
})
.filter(transformer => transformer.pattern != null)
}
interface RegexTransformer {
pattern: RegExp | null
target: string
}