Skip to content

Commit

Permalink
- introduce the ability to reference parent / child PR relationships
Browse files Browse the repository at this point in the history
  - FIX #1074
- optimize placeholder removal
- only keep placeholders in array if we have values
  • Loading branch information
mikepenz authored Jun 4, 2023
1 parent b691df6 commit 831ac3e
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 9 deletions.
22 changes: 21 additions & 1 deletion __tests__/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ pullRequestsWithLabels.push(
repoName: 'test-repo',
labels: ['issue', 'fix'],
milestone: '',
body: 'no magic body for this matter',
body: 'no magic body for this matter - #1',
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
Expand Down Expand Up @@ -399,6 +399,26 @@ it('Deduplicate duplicated PRs DESC', async () => {
)
})

it('Reference PRs', async () => {
const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.categories = [
{
title: '',
labels: []
}
]
customConfig.pr_template = "${{NUMBER}} -- ${{REFERENCED[*].number}}"
customConfig.reference = {
pattern: '.*\ \#(.).*',
on_property: 'body',
method: 'replace',
target: '$1'
}
expect(buildChangelogTest(customConfig, pullRequestsWithLabels)).toStrictEqual(
`1 -- 2\n4 -- \n3 -- \n\n`
)
})

it('Use empty_content for empty category', async () => {
const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.categories = [
Expand Down
88 changes: 85 additions & 3 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions pr-collector/src/pullRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ export interface CommentInfo {
state: string | undefined
}

export const EMPTY_PULL_REQUEST_INFO: PullRequestInfo = {
number: 0,
title: "",
htmlURL: "",
baseBranch: "",
mergedAt: undefined,
createdAt: moment(),
mergeCommitSha: "",
author: "",
repoName: "",
labels: [],
milestone: "",
body: "",
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'open'
}

export const EMPTY_COMMENT_INFO: CommentInfo = {
id: 0,
htmlURL: '',
Expand Down
1 change: 1 addition & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface Configuration extends PullConfiguration {
ignore_labels: string[]
label_extractor: Extractor[]
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.
transformers: Transformer[]
tag_resolver: TagResolver
base_branches: string[]
Expand Down
88 changes: 84 additions & 4 deletions src/transform.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import * as core from '@actions/core'
import {Category, Configuration, Placeholder, Property} from './configuration'
import {createOrSet, haveCommonElementsArr, haveEveryElementsArr} from './utils'
import {CommentInfo, EMPTY_COMMENT_INFO, PullRequestInfo, retrieveProperty, sortPullRequests} from 'github-pr-collector/lib/pullRequests'
import {
CommentInfo,
EMPTY_COMMENT_INFO,
EMPTY_PULL_REQUEST_INFO,
PullRequestInfo,
retrieveProperty,
sortPullRequests
} from 'github-pr-collector/lib/pullRequests'
import {DiffInfo} from 'github-pr-collector/lib/commits'
import {validateTransformer} from 'github-pr-collector/lib/regexUtils'
import {Transformer, RegexTransformer} from 'github-pr-collector/lib/types'
Expand All @@ -10,8 +17,14 @@ import {matchesRules} from './regexUtils'

const EMPTY_MAP = new Map<string, string>()

export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], options: ReleaseNotesOptions): string {
export interface PullRequestData extends PullRequestInfo {
childPrs?: PullRequestInfo[]
}

export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], options: ReleaseNotesOptions): string {
core.startGroup('📦 Build changelog')

let prs: PullRequestData[] = origPrs
if (prs.length === 0) {
core.warning(`⚠️ No pull requests found`)
const result = replaceEmptyTemplate(options.configuration.empty_template, options)
Expand All @@ -25,10 +38,47 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
prs = sortPullRequests(prs, sort)
core.info(`ℹ️ Sorted all pull requests ascending: ${JSON.stringify(sort)}`)

// establish parent child PR relations
if (config.reference !== undefined) {
const reference = validateTransformer(config.reference)
if (reference !== null) {
core.info(`ℹ️ Identifying PR references using \`reference\``)

const mapped = new Map<number, PullRequestData>()
for (const pr of prs) {
mapped.set(pr.number, pr)
}

const remappedPrs: PullRequestData[] = []
for (const pr of prs) {
const extracted = extractValues(pr, reference, 'reference')
if (extracted !== null && extracted.length > 0) {
const foundNumber = parseInt(extracted[0])
const valid = !isNaN(foundNumber)
const parent = mapped.get(foundNumber)
if (valid && parent !== undefined) {
if (parent.childPrs === undefined) {
parent.childPrs = []
}
parent.childPrs.push(pr)
} else {
if (!valid) core.warning(`⚠️ Extracted reference 'isNaN': ${extracted}`)
remappedPrs.push(pr)
}
} else {
remappedPrs.push(pr)
}
}
prs = remappedPrs
} else {
core.warning(`⚠️ Configured \`reference\` invalid.`)
}
}

// drop duplicate pull requests
if (config.duplicate_filter !== undefined) {
const extractor = validateTransformer(config.duplicate_filter)
if (extractor != null) {
if (extractor !== null) {
core.info(`ℹ️ Remove duplicated pull requests using \`duplicate_filter\``)

const deduplicatedMap = new Map<string, PullRequestInfo>()
Expand Down Expand Up @@ -283,6 +333,7 @@ export function buildChangelog(diffInfo: DiffInfo, prs: PullRequestInfo[], optio
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
Expand Down Expand Up @@ -322,14 +373,15 @@ function fillAdditionalPlaceholders(
}

function fillPrTemplate(
pr: PullRequestInfo,
pr: PullRequestData,
template: string,
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
placeholderPrMap: Map<string, string[]> /* map to keep replaced placeholder values with their key */,
configuration: Configuration
): string {
const arrayPlaceholderMap = new Map<string, string>()
fillReviewPlaceholders(arrayPlaceholderMap, 'REVIEWS', pr.reviews || [])
fillChildPrPlaceholders(arrayPlaceholderMap, 'REFERENCED', pr.childPrs || [])
const placeholderMap = new Map<string, string>()
placeholderMap.set('NUMBER', pr.number.toString())
placeholderMap.set('TITLE', pr.title)
Expand Down Expand Up @@ -419,6 +471,7 @@ function fillArrayPlaceholders(
key: string,
values: string[]
): void {
if (values.length === 0) return
for (let i = 0; i < values.length; i++) {
placeholderMap.set(`${key}[${i}]`, values[i])
}
Expand All @@ -430,6 +483,7 @@ function fillReviewPlaceholders(
parentKey: string,
values: CommentInfo[]
): void {
if (values.length === 0) return
// retrieve the keys from the CommentInfo object
for (const childKey of Object.keys(EMPTY_COMMENT_INFO)) {
for (let i = 0; i < values.length; i++) {
Expand All @@ -442,6 +496,24 @@ function fillReviewPlaceholders(
}
}

function fillChildPrPlaceholders(
placeholderMap: Map<string, string> /* placeholderKey and original value */,
parentKey: string,
values: PullRequestInfo[]
): void {
if (values.length === 0) return
// retrieve the keys from the PullRequestInfo object
for (const childKey of Object.keys(EMPTY_PULL_REQUEST_INFO)) {
for (let i = 0; i < values.length; i++) {
placeholderMap.set(`${parentKey}[${i}].${childKey}`, values[i][childKey as keyof PullRequestInfo]?.toLocaleString('en') || '')
}
placeholderMap.set(
`${parentKey}[*].${childKey}`,
values.map(value => value[childKey as keyof PullRequestInfo]?.toLocaleString('en') || '').join(', ')
)
}
}

function replacePrPlaceholders(
template: string,
placeholderPrMap: Map<string, string[]> /* map with all pr related custom placeholder values */,
Expand All @@ -467,6 +539,14 @@ function cleanupPrPlaceholders(template: string, placeholders: Map<string, Place
return transformed
}

function cleanupPlaceholders(template: string): string {
let transformed = template
for (const phs of ['REVIEWS', 'REFERENCED', 'ASSIGNEES', 'REVIEWERS', 'APPROVERS']) {
transformed = transformed.replaceAll(new RegExp(`\\$\\{\\{${phs}\\[.+?\\]\\..*?\\}\\}`, 'gu'), '')
}
return transformed
}

function transform(filled: string, transformers: RegexTransformer[]): string {
if (transformers.length === 0) {
return filled
Expand Down

0 comments on commit 831ac3e

Please sign in to comment.