- update replacement logic for array and comment types (fix cleanup if no match)

- adjust style definition - 140 line length
This commit is contained in:
Mike Penz
2022-12-09 14:09:40 +00:00
committed by GitHub
parent 3e744d00f1
commit 27d0a0a53c
9 changed files with 145 additions and 155 deletions
+1 -8
View File
@@ -40,14 +40,7 @@ export interface Transformer extends Regex {
}
export interface Extractor extends Transformer {
on_property?:
| ('title' | 'author' | 'milestone' | 'body' | 'branch')[]
| 'title'
| 'author'
| 'milestone'
| 'body'
| 'branch'
| undefined // retrieve the property to extract the value from
on_property?: ('title' | 'author' | 'milestone' | 'body' | 'branch')[] | 'title' | 'author' | 'milestone' | 'body' | 'branch' | undefined // retrieve the property to extract the value from
method?: 'replace' | 'match' | undefined // the method to use to extract the value, `match` will not use the `target` property
on_empty?: string | undefined // in case the regex results in an empty string, this value is gonna be used instead (only for label_extractor currently)
}
+1 -4
View File
@@ -232,10 +232,7 @@ function attachSpeciaLabels(status: 'open' | 'merged', labels: Set<string>): Set
return labels
}
const mapPullRequest = (
pr: PullData | Unpacked<PullsListData>,
status: 'open' | 'merged' = 'open'
): PullRequestInfo => ({
const mapPullRequest = (pr: PullData | Unpacked<PullsListData>, status: 'open' | 'merged' = 'open'): PullRequestInfo => ({
number: pr.number,
title: pr.title,
htmlURL: pr.html_url,
+4 -15
View File
@@ -62,10 +62,7 @@ export class ReleaseNotes {
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`)
return replaceEmptyTemplate(
this.options.configuration.empty_template || DefaultConfiguration.empty_template,
this.options
)
return replaceEmptyTemplate(this.options.configuration.empty_template || DefaultConfiguration.empty_template, this.options)
}
core.startGroup('📦 Build changelog')
@@ -128,10 +125,7 @@ export class ReleaseNotes {
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`)
const prCommits = filterCommits(
commits,
configuration.exclude_merge_branches || DefaultConfiguration.exclude_merge_branches
)
const prCommits = filterCommits(commits, configuration.exclude_merge_branches || DefaultConfiguration.exclude_merge_branches)
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`)
@@ -181,9 +175,7 @@ export class ReleaseNotes {
})
if (baseBranches.length !== 0) {
core.info(
`️ Retrieved ${mergedPullRequests.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`
)
core.info(`️ Retrieved ${mergedPullRequests.length} PRs for ${owner}/${repo} filtered by the 'base_branches' configuration.`)
}
if (fetchReviewers) {
@@ -224,10 +216,7 @@ export class ReleaseNotes {
return [diffInfo, []]
}
const prCommits = filterCommits(
commits,
configuration.exclude_merge_branches || DefaultConfiguration.exclude_merge_branches
)
const prCommits = filterCommits(commits, configuration.exclude_merge_branches || DefaultConfiguration.exclude_merge_branches)
core.info(`️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`)
+2 -6
View File
@@ -52,9 +52,7 @@ export class Tags {
}
}
core.info(
`️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`
)
core.info(`️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`)
return tagsInfo
}
@@ -73,9 +71,7 @@ export class Tags {
tagInfo.date = moment(release.created_at)
core.info(`️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API`)
} catch (error) {
core.info(
`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`
)
core.info(`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`)
const gitHelper = await createCommandManager(repositoryPath)
const creationTimeString = await gitHelper.tagCreation(tagInfo.name)
const creationTime = moment(creationTimeString)
+79 -68
View File
@@ -13,6 +13,8 @@ export interface RegexTransformer {
onEmpty?: string | undefined
}
const EMPTY_MAP = new Map<string, string>()
export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], options: ReleaseNotesOptions): string {
// sort to target order
const config = options.configuration
@@ -126,9 +128,7 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
const prNum = pr.number
const prLabels = pr.labels
const excludeLabels = JSON.stringify(category.exclude_labels)
core.debug(
`PR ${prNum} with labels: ${prLabels} excluded from category via exclude label: ${excludeLabels}`
)
core.debug(`PR ${prNum} with labels: ${prLabels} excluded from category via exclude label: ${excludeLabels}`)
}
continue // one of the exclude labels matched, skip the PR for this category
}
@@ -253,9 +253,9 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
fillAdditionalPlaceholders(options, placeholderMap)
let transformedChangelog = config.template || DefaultConfiguration.template
transformedChangelog = replacePlaceholders(transformedChangelog, placeholderMap, placeholders, placeholderPrMap)
transformedChangelog = replacePlaceholders(transformedChangelog, EMPTY_MAP, placeholderMap, placeholders, placeholderPrMap)
transformedChangelog = replacePrPlaceholders(transformedChangelog, placeholderPrMap)
transformedChangelog = cleanupPrPlaceHolders(transformedChangelog, placeholders)
transformedChangelog = cleanupPrPlaceholders(transformedChangelog, placeholders)
core.info(`️ Filled template`)
return transformedChangelog
}
@@ -267,7 +267,7 @@ export function replaceEmptyTemplate(template: string, options: ReleaseNotesOpti
}
const placeholderMap = new Map<string, string>()
fillAdditionalPlaceholders(options, placeholderMap)
return replacePlaceholders(template, placeholderMap, placeholders)
return replacePlaceholders(template, new Map<string, string>(), placeholderMap, placeholders)
}
function fillAdditionalPlaceholders(
@@ -299,11 +299,8 @@ function fillPrTemplate(
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
placeholderPrMap: Map<string, string[]> /* map to keep replaced placeholder values with their key */
): string {
let transformed = replaceArrayPlaceholders(template, 'ASSIGNEES', pr.assignees || [])
transformed = replaceArrayPlaceholders(transformed, 'REVIEWERS', pr.requestedReviewers || [])
transformed = replaceArrayPlaceholders(transformed, 'APPROVERS', pr.approvedReviewers || [])
transformed = replaceReviewPlaceholders(transformed, 'REVIEWS', pr.reviews || [])
const arrayPlaceholderMap = new Map<string, string>()
fillReviewPlaceholders(arrayPlaceholderMap, 'REVIEWS', pr.reviews || [])
const placeholderMap = new Map<string, string>()
placeholderMap.set('NUMBER', pr.number.toString())
placeholderMap.set('TITLE', pr.title)
@@ -316,49 +313,66 @@ function fillPrTemplate(
placeholderMap.set('LABELS', [...pr.labels]?.filter(l => !l.startsWith('--rcba-'))?.join(', ') || '')
placeholderMap.set('MILESTONE', pr.milestone || '')
placeholderMap.set('BODY', pr.body)
fillArrayPlaceholders(arrayPlaceholderMap, 'ASSIGNEES', pr.assignees || [])
placeholderMap.set('ASSIGNEES', pr.assignees?.join(', ') || '')
fillArrayPlaceholders(arrayPlaceholderMap, 'REVIEWERS', pr.requestedReviewers || [])
placeholderMap.set('REVIEWERS', pr.requestedReviewers?.join(', ') || '')
fillArrayPlaceholders(arrayPlaceholderMap, 'APPROVERS', pr.approvedReviewers || [])
placeholderMap.set('APPROVERS', pr.approvedReviewers?.join(', ') || '')
placeholderMap.set('BRANCH', pr.branch || '')
placeholderMap.set('BASE_BRANCH', pr.baseBranch)
return replacePlaceholders(transformed, placeholderMap, placeholders, placeholderPrMap)
return replacePlaceholders(template, arrayPlaceholderMap, placeholderMap, placeholders, placeholderPrMap)
}
function replacePlaceholders(
template: string,
arrayPlaceholderMap: Map<string, string> /* arrayPlaceholderKey and original value */,
placeholderMap: Map<string, string> /* placeholderKey and original value */,
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
placeholderPrMap?: Map<string, string[]> /* map to keep replaced placeholder values with their key */
): string {
let transformed = template
// replace array placeholders first
for (const [key, value] of arrayPlaceholderMap) {
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap)
}
// replace traditional placeholders
for (const [key, value] of placeholderMap) {
transformed = transformed.replaceAll(`\${{${key}}}`, value)
transformed = handlePlaceholder(transformed, key, value, placeholders, placeholderPrMap)
}
// replace custom placeholders
const phs = placeholders.get(key)
if (phs) {
for (const placeholder of phs) {
const transformer = validateTransformer(placeholder.transformer)
if (transformer?.pattern) {
const extractedValue = value.replace(transformer.pattern, transformer.target)
// note: `.replace` will return the full string again if there was no match
if (
extractedValue &&
(extractedValue !== value || (extractedValue === value && value.match(transformer.pattern)))
) {
if (placeholderPrMap) {
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
}
transformed = transformed.replaceAll(`\${{${placeholder.name}}}`, extractedValue)
return transformed
}
if (core.isDebug()) {
core.debug(` Custom Placeholder successfully matched data - ${extractValues} (${placeholder.name})`)
}
} else if (core.isDebug() && extractedValue === value) {
core.debug(
` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`
)
function handlePlaceholder(
template: string,
key: string,
value: string,
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
placeholderPrMap?: Map<string, string[]> /* map to keep replaced placeholder values with their key */
): string {
let transformed = template.replaceAll(`\${{${key}}}`, value)
// replace custom placeholders
const phs = placeholders.get(key)
if (phs) {
for (const placeholder of phs) {
const transformer = validateTransformer(placeholder.transformer)
if (transformer?.pattern) {
const extractedValue = value.replace(transformer.pattern, transformer.target)
// note: `.replace` will return the full string again if there was no match
if (extractedValue && (extractedValue !== value || (extractedValue === value && value.match(transformer.pattern)))) {
if (placeholderPrMap) {
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
}
transformed = transformed.replaceAll(`\${{${placeholder.name}}}`, extractedValue)
if (core.isDebug()) {
core.debug(` Custom Placeholder successfully matched data - ${extractValues} (${placeholder.name})`)
}
} else if (core.isDebug() && extractedValue === value) {
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`)
}
}
}
@@ -366,6 +380,34 @@ function replacePlaceholders(
return transformed
}
function fillArrayPlaceholders(
placeholderMap: Map<string, string> /* placeholderKey and original value */,
key: string,
values: string[]
): void {
for (let i = 0; i < values.length; i++) {
placeholderMap.set(`\${{${key}[${i}]}}`, values[i])
}
placeholderMap.set(`\${{${key}[*]}}`, values.join(', '))
}
function fillReviewPlaceholders(
placeholderMap: Map<string, string> /* placeholderKey and original value */,
parentKey: string,
values: CommentInfo[]
): void {
// retrieve the keys from the CommentInfo object
for (const childKey of Object.keys(EMPTY_COMMENT_INFO)) {
for (let i = 0; i < values.length; i++) {
placeholderMap.set(`\${{${parentKey}[${i}].${childKey}}}`, values[i][childKey as keyof CommentInfo]?.toLocaleString('en') || '')
}
placeholderMap.set(
`\${{${parentKey}[*].${childKey}}}`,
values.map(value => value[childKey as keyof CommentInfo]?.toLocaleString('en') || '').join(', ')
)
}
}
function replacePrPlaceholders(
template: string,
placeholderPrMap: Map<string, string[]> /* map with all pr related custom placeholder values */
@@ -380,38 +422,7 @@ function replacePrPlaceholders(
return transformed
}
function replaceArrayPlaceholders(template: string, key: string, values: string[]): string {
let transformed = template
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(`\${{${key}[${i}]}}`, values[i])
}
transformed = transformed.replaceAll(`\${{${key}[*]}}`, values.join(', '))
return transformed
}
function replaceReviewPlaceholders(template: string, parentKey: string, values: CommentInfo[]): string {
let transformed = template
// retrieve the keys from the CommentInfo object
for (const childKey of Object.keys(EMPTY_COMMENT_INFO)) {
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(
`\${{${parentKey}[${i}].${childKey}}}`,
values[i][childKey as keyof CommentInfo]?.toLocaleString('en') || ''
)
}
transformed = transformed.replaceAll(
`\${{${parentKey}[*].${childKey}}}`,
values.map(value => value[childKey as keyof CommentInfo]?.toLocaleString('en') || '').join(', ')
)
}
return transformed
}
function cleanupPrPlaceHolders(
template: string,
placeholders: Map<string, Placeholder[]> /* placeholders to apply */
): string {
function cleanupPrPlaceholders(template: string, placeholders: Map<string, Placeholder[]> /* placeholders to apply */): string {
let transformed = template
for (const [, phs] of placeholders) {
for (const ph of phs) {