Merge pull request #453 from mikepenz/feature/450
Introduce `method` configuration for `label_extractor` | Additional `transform.test.ts` cases
This commit is contained in:
@@ -296,8 +296,9 @@ Table of descriptions for the `configuration.json` options to configure the resu
|
|||||||
| empty_template | Template to pick if no changes are detected. See [Template placeholders](#template-placeholders) for possible values |
|
| empty_template | Template to pick if no changes are detected. See [Template placeholders](#template-placeholders) for possible values |
|
||||||
| label_extractor | An array of `transform` specifications, offering a flexible API to extract additinal labels from a PR (Default: `body`, Default in commit mode: `commit message`). |
|
| label_extractor | An array of `transform` specifications, offering a flexible API to extract additinal labels from a PR (Default: `body`, Default in commit mode: `commit message`). |
|
||||||
| label_extractor.pattern | A `regex` pattern, extracting values of the change message. |
|
| label_extractor.pattern | A `regex` pattern, extracting values of the change message. |
|
||||||
| label_extractor.target | The result pattern. The result text will be used as label. If empty, no label is created. |
|
| label_extractor.target | The result pattern. The result text will be used as label. If empty, no label is created. (Unused for `match` method) |
|
||||||
| label_extractor.on_property | The property to retrieve the text from. This is optional. Defaults to: `body`. Alternative values: `title`, `author`, `milestone`. |
|
| label_extractor.on_property | The property to retrieve the text from. This is optional. Defaults to: `body`. Alternative values: `title`, `author`, `milestone`. |
|
||||||
|
| label_extractor.method | The extraction method used. Defaults to: `replace`. Alternative value: `match`. The method specified references the JavaScript String method. |
|
||||||
| label_extractor.flags | Defines the regex flags specified for the pattern. Default: `gu` |
|
| label_extractor.flags | Defines the regex flags specified for the pattern. Default: `gu` |
|
||||||
| transformers | An array of `transform` specifications, offering a flexible API to modify the text per pull request. This is applied on the change text created with `pr_template`. `transformers` are executed per change, in the order specified |
|
| transformers | An array of `transform` specifications, offering a flexible API to modify the text per pull request. This is applied on the change text created with `pr_template`. `transformers` are executed per change, in the order specified |
|
||||||
| transformer.pattern | A `regex` pattern, extracting values of the change message. |
|
| transformer.pattern | A `regex` pattern, extracting values of the change message. |
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import {buildChangelog} from '../src/transform'
|
||||||
|
import { PullRequestInfo } from '../src/pullRequests'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { DefaultConfiguration } from '../src/configuration';
|
||||||
|
|
||||||
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
|
let configuration = DefaultConfiguration
|
||||||
|
configuration.categories = [
|
||||||
|
{
|
||||||
|
"title": "## 🚀 Features",
|
||||||
|
"labels": ["[Feature]"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "## 🐛 Fixes",
|
||||||
|
"labels": ["[Bug]", "[Issue]"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "## 🧪 Tests",
|
||||||
|
"labels": ["[Test]"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let mergedPullRequests: PullRequestInfo[] = []
|
||||||
|
mergedPullRequests.push({
|
||||||
|
number: 1,
|
||||||
|
title: "[Feature][AB-1234] - this is a PR 1 title message",
|
||||||
|
htmlURL: "",
|
||||||
|
baseBranch: "",
|
||||||
|
mergedAt: moment(),
|
||||||
|
mergeCommitSha: "sha1",
|
||||||
|
author: "Mike",
|
||||||
|
repoName: "test-repo",
|
||||||
|
labels: new Set<string>(),
|
||||||
|
milestone: "",
|
||||||
|
body: "no magic body for this matter",
|
||||||
|
assignees: [],
|
||||||
|
requestedReviewers: []
|
||||||
|
}, {
|
||||||
|
number: 2,
|
||||||
|
title: "[Issue][AB-4321] - this is a PR 2 title message",
|
||||||
|
htmlURL: "",
|
||||||
|
baseBranch: "",
|
||||||
|
mergedAt: moment(),
|
||||||
|
mergeCommitSha: "sha1",
|
||||||
|
author: "Mike",
|
||||||
|
repoName: "test-repo",
|
||||||
|
labels: new Set<string>(),
|
||||||
|
milestone: "",
|
||||||
|
body: "no magic body for this matter",
|
||||||
|
assignees: [],
|
||||||
|
requestedReviewers: []
|
||||||
|
}, {
|
||||||
|
number: 3,
|
||||||
|
title: "[Issue][Feature][AB-1234321] - this is a PR 3 title message",
|
||||||
|
htmlURL: "",
|
||||||
|
baseBranch: "",
|
||||||
|
mergedAt: moment(),
|
||||||
|
mergeCommitSha: "sha1",
|
||||||
|
author: "Mike",
|
||||||
|
repoName: "test-repo",
|
||||||
|
labels: new Set<string>(),
|
||||||
|
milestone: "",
|
||||||
|
body: "no magic body for this matter",
|
||||||
|
assignees: [],
|
||||||
|
requestedReviewers: []
|
||||||
|
}, {
|
||||||
|
number: 4,
|
||||||
|
title: "[AB-404] - not found label",
|
||||||
|
htmlURL: "",
|
||||||
|
baseBranch: "",
|
||||||
|
mergedAt: moment(),
|
||||||
|
mergeCommitSha: "sha1",
|
||||||
|
author: "Mike",
|
||||||
|
repoName: "test-repo",
|
||||||
|
labels: new Set<string>(),
|
||||||
|
milestone: "",
|
||||||
|
body: "no magic body for this matter",
|
||||||
|
assignees: [],
|
||||||
|
requestedReviewers: []
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Extract label from title, combined regex', async () => {
|
||||||
|
configuration.label_extractor = [
|
||||||
|
{
|
||||||
|
"pattern": ".*(\\[Feature\\]|\\[Issue\\]).*",
|
||||||
|
"target": "$1",
|
||||||
|
"on_property": "title"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const resultChangelog = buildChangelog(
|
||||||
|
mergedPullRequests,
|
||||||
|
{
|
||||||
|
owner: "mikepenz",
|
||||||
|
repo: "test-repo",
|
||||||
|
fromTag: "1.0.0",
|
||||||
|
toTag: "2.0.0",
|
||||||
|
failOnError: false,
|
||||||
|
commitMode: false,
|
||||||
|
configuration: configuration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resultChangelog).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\n`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Extract label from title, split regex', async () => {
|
||||||
|
configuration.label_extractor = [
|
||||||
|
{
|
||||||
|
"pattern": ".*(\\[Feature\\]).*",
|
||||||
|
"target": "$1",
|
||||||
|
"on_property": "title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": ".*(\\[Issue\\]).*",
|
||||||
|
"target": "$1",
|
||||||
|
"on_property": "title"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const resultChangelog = buildChangelog(
|
||||||
|
mergedPullRequests,
|
||||||
|
{
|
||||||
|
owner: "mikepenz",
|
||||||
|
repo: "test-repo",
|
||||||
|
fromTag: "1.0.0",
|
||||||
|
toTag: "2.0.0",
|
||||||
|
failOnError: false,
|
||||||
|
commitMode: false,
|
||||||
|
configuration: configuration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resultChangelog).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`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Extract label from title, match', async () => {
|
||||||
|
configuration.label_extractor = [
|
||||||
|
{
|
||||||
|
"pattern": "\\[Feature\\]",
|
||||||
|
"on_property": "title",
|
||||||
|
"method": "match"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "\\[Issue\\]",
|
||||||
|
"on_property": "title",
|
||||||
|
"method": "match"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const resultChangelog = buildChangelog(
|
||||||
|
mergedPullRequests,
|
||||||
|
{
|
||||||
|
owner: "mikepenz",
|
||||||
|
repo: "test-repo",
|
||||||
|
fromTag: "1.0.0",
|
||||||
|
toTag: "2.0.0",
|
||||||
|
failOnError: false,
|
||||||
|
commitMode: false,
|
||||||
|
configuration: configuration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resultChangelog).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`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Extract label from title, match multiple', async () => {
|
||||||
|
configuration.label_extractor = [
|
||||||
|
{
|
||||||
|
"pattern": "\\[Feature\\]|\\[Issue\\]",
|
||||||
|
"on_property": "title",
|
||||||
|
"method": "match"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const resultChangelog = buildChangelog(
|
||||||
|
mergedPullRequests,
|
||||||
|
{
|
||||||
|
owner: "mikepenz",
|
||||||
|
repo: "test-repo",
|
||||||
|
fromTag: "1.0.0",
|
||||||
|
toTag: "2.0.0",
|
||||||
|
failOnError: false,
|
||||||
|
commitMode: false,
|
||||||
|
configuration: configuration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resultChangelog).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`)
|
||||||
|
})
|
||||||
+62
-1289
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
@@ -22,12 +22,13 @@ export interface Category {
|
|||||||
|
|
||||||
export interface Transformer {
|
export interface Transformer {
|
||||||
pattern: string
|
pattern: string
|
||||||
target: string
|
target?: string
|
||||||
flags?: string
|
flags?: string // the regex flag to use for RegExp
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Extractor extends Transformer {
|
export interface Extractor extends Transformer {
|
||||||
on_property: 'title' | 'author' | 'milestone' | 'body' | undefined
|
on_property?: 'title' | 'author' | 'milestone' | 'body' | undefined // retrieve the property to extract the value from
|
||||||
|
method?: 'replace' | 'match' | undefined // the method to use to extract the value
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagResolver {
|
export interface TagResolver {
|
||||||
|
|||||||
+1
-7
@@ -20,8 +20,6 @@ export class ReleaseNotes {
|
|||||||
constructor(private octokit: Octokit, private options: ReleaseNotesOptions) {}
|
constructor(private octokit: Octokit, private options: ReleaseNotesOptions) {}
|
||||||
|
|
||||||
async pull(): Promise<string | null> {
|
async pull(): Promise<string | null> {
|
||||||
const {configuration} = this.options
|
|
||||||
|
|
||||||
let mergedPullRequests: PullRequestInfo[]
|
let mergedPullRequests: PullRequestInfo[]
|
||||||
if (!this.options.commitMode) {
|
if (!this.options.commitMode) {
|
||||||
core.startGroup(`🚀 Load pull requests`)
|
core.startGroup(`🚀 Load pull requests`)
|
||||||
@@ -40,11 +38,7 @@ export class ReleaseNotes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
core.startGroup('📦 Build changelog')
|
core.startGroup('📦 Build changelog')
|
||||||
const resultChangelog = buildChangelog(
|
const resultChangelog = buildChangelog(mergedPullRequests, this.options)
|
||||||
mergedPullRequests,
|
|
||||||
configuration,
|
|
||||||
this.options
|
|
||||||
)
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
return resultChangelog
|
return resultChangelog
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-10
@@ -1,7 +1,6 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {
|
import {
|
||||||
Category,
|
Category,
|
||||||
Configuration,
|
|
||||||
DefaultConfiguration,
|
DefaultConfiguration,
|
||||||
Extractor,
|
Extractor,
|
||||||
Transformer
|
Transformer
|
||||||
@@ -11,10 +10,10 @@ import {ReleaseNotesOptions} from './releaseNotes'
|
|||||||
|
|
||||||
export function buildChangelog(
|
export function buildChangelog(
|
||||||
prs: PullRequestInfo[],
|
prs: PullRequestInfo[],
|
||||||
config: Configuration,
|
|
||||||
options: ReleaseNotesOptions
|
options: ReleaseNotesOptions
|
||||||
): string {
|
): string {
|
||||||
// sort to target order
|
// sort to target order
|
||||||
|
const config = options.configuration
|
||||||
const sort = config.sort || DefaultConfiguration.sort
|
const sort = config.sort || DefaultConfiguration.sort
|
||||||
const sortAsc = sort.toUpperCase() === 'ASC'
|
const sortAsc = sort.toUpperCase() === 'ASC'
|
||||||
prs = sortPullRequests(prs, sortAsc)
|
prs = sortPullRequests(prs, sortAsc)
|
||||||
@@ -25,7 +24,7 @@ export function buildChangelog(
|
|||||||
for (const extractor of labelExtractors) {
|
for (const extractor of labelExtractors) {
|
||||||
if (extractor.pattern != null) {
|
if (extractor.pattern != null) {
|
||||||
for (const pr of prs) {
|
for (const pr of prs) {
|
||||||
let label
|
let onValue
|
||||||
if (extractor.onProperty !== undefined) {
|
if (extractor.onProperty !== undefined) {
|
||||||
let value: string = pr[extractor.onProperty]
|
let value: string = pr[extractor.onProperty]
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
@@ -34,16 +33,27 @@ export function buildChangelog(
|
|||||||
)
|
)
|
||||||
value = pr['body']
|
value = pr['body']
|
||||||
}
|
}
|
||||||
label = value.replace(extractor.pattern, extractor.target)
|
onValue = value
|
||||||
} else {
|
} else {
|
||||||
label = pr.body.replace(extractor.pattern, extractor.target)
|
onValue = pr.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extractor.method === 'match') {
|
||||||
|
const lables = onValue.match(extractor.pattern)
|
||||||
|
if (lables !== null) {
|
||||||
|
for (const label of lables) {
|
||||||
|
pr.labels.add(label.toLocaleLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const label = onValue.replace(extractor.pattern, extractor.target)
|
||||||
if (label !== '') {
|
if (label !== '') {
|
||||||
pr.labels.add(label.toLocaleLowerCase())
|
pr.labels.add(label.toLocaleLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const validatedTransformers = validateTransformers(config.transformers)
|
const validatedTransformers = validateTransformers(config.transformers)
|
||||||
const transformedMap = new Map<PullRequestInfo, string>()
|
const transformedMap = new Map<PullRequestInfo, string>()
|
||||||
@@ -252,8 +262,10 @@ function validateTransformers(
|
|||||||
.map(transformer => {
|
.map(transformer => {
|
||||||
try {
|
try {
|
||||||
let onProperty = undefined
|
let onProperty = undefined
|
||||||
|
let method = undefined
|
||||||
if (transformer.hasOwnProperty('on_property')) {
|
if (transformer.hasOwnProperty('on_property')) {
|
||||||
onProperty = (transformer as Extractor).on_property
|
onProperty = (transformer as Extractor).on_property
|
||||||
|
method = (transformer as Extractor).method
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -261,15 +273,15 @@ function validateTransformers(
|
|||||||
transformer.pattern.replace('\\\\', '\\'),
|
transformer.pattern.replace('\\\\', '\\'),
|
||||||
transformer.flags ?? 'gu'
|
transformer.flags ?? 'gu'
|
||||||
),
|
),
|
||||||
target: transformer.target,
|
target: transformer.target || '',
|
||||||
onProperty
|
onProperty,
|
||||||
|
method
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
core.warning(`⚠️ Bad replacer regex: ${transformer.pattern}`)
|
core.warning(`⚠️ Bad replacer regex: ${transformer.pattern}`)
|
||||||
return {
|
return {
|
||||||
pattern: null,
|
pattern: null,
|
||||||
target: '',
|
target: ''
|
||||||
onProperty: undefined
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -279,5 +291,6 @@ function validateTransformers(
|
|||||||
interface RegexTransformer {
|
interface RegexTransformer {
|
||||||
pattern: RegExp | null
|
pattern: RegExp | null
|
||||||
target: string
|
target: string
|
||||||
onProperty: 'title' | 'author' | 'milestone' | 'body' | undefined
|
onProperty?: 'title' | 'author' | 'milestone' | 'body' | undefined
|
||||||
|
method?: 'replace' | 'match' | undefined
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user