- refactor regex handling for the different usecases to allow providing the preferred method
- introduce new `exec` and `execAll` variants which support named groups - introduce `replaceAll` in addition to `replace` - update test cases - cleanup code
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
import { transformStringToValue, validateRegex } from '../src/pr-collector/regexUtils'
|
||||||
|
import { Regex } from '../src/pr-collector/types'
|
||||||
|
|
||||||
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
|
it('Replace into target', async () => {
|
||||||
|
const regex: Regex = {
|
||||||
|
pattern: '.*(\\[Feature\\]|\\[Issue\\]).*',
|
||||||
|
target: '$1',
|
||||||
|
}
|
||||||
|
const validatedRegex = validateRegex(regex)
|
||||||
|
expect(validateRegex).not.toBeNull()
|
||||||
|
expect(transformStringToValue("[Feature] TEST", validatedRegex!!)).toStrictEqual(`[Feature]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Replace all into target', async () => {
|
||||||
|
const regex: Regex = {
|
||||||
|
pattern: '.*(\\[Feature\\]|\\[Issue\\]).*',
|
||||||
|
method: 'replaceAll',
|
||||||
|
target: '$1',
|
||||||
|
}
|
||||||
|
const validatedRegex = validateRegex(regex)
|
||||||
|
expect(validateRegex).not.toBeNull()
|
||||||
|
expect(transformStringToValue("[Feature] TEST", validatedRegex!!)).toStrictEqual(`[Feature]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Match without target', async () => {
|
||||||
|
const regex: Regex = {
|
||||||
|
pattern: '\\[Feature\\]|\\[Issue\\]',
|
||||||
|
method: 'match'
|
||||||
|
}
|
||||||
|
const validatedRegex = validateRegex(regex)
|
||||||
|
expect(validateRegex).not.toBeNull()
|
||||||
|
expect(transformStringToValue("[Feature] TEST", validatedRegex!!)).toStrictEqual(`[Feature]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Match into target', async () => {
|
||||||
|
const regex: Regex = {
|
||||||
|
pattern: '(?<label>\\[Feature\\]|\\[Issue\\])',
|
||||||
|
method: 'match',
|
||||||
|
target: '$1',
|
||||||
|
}
|
||||||
|
const validatedRegex = validateRegex(regex)
|
||||||
|
expect(validateRegex).not.toBeNull()
|
||||||
|
expect(transformStringToValue("[Feature] TEST", validatedRegex!!)).toStrictEqual(`[Feature]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Match into named group', async () => {
|
||||||
|
const regex: Regex = {
|
||||||
|
pattern: '(?<label>\\[Feature\\]|\\[Issue\\])',
|
||||||
|
method: 'match',
|
||||||
|
target: 'label',
|
||||||
|
}
|
||||||
|
const validatedRegex = validateRegex(regex)
|
||||||
|
expect(validateRegex).not.toBeNull()
|
||||||
|
expect(transformStringToValue("[Feature] TEST", validatedRegex!!)).toStrictEqual(`[Feature]`)
|
||||||
|
})
|
||||||
+12
-7
@@ -1,4 +1,5 @@
|
|||||||
import { validateTransformer } from '../src/pr-collector/regexUtils'
|
import { TagResolver } from '../src/configuration'
|
||||||
|
import { validateRegex } from '../src/pr-collector/regexUtils'
|
||||||
import {filterTags, prepareAndSortTags, TagInfo, transformTags} from '../src/pr-collector/tags'
|
import {filterTags, prepareAndSortTags, TagInfo, transformTags} from '../src/pr-collector/tags'
|
||||||
|
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
@@ -100,14 +101,16 @@ it('Should filter tags correctly using the regex', async () => {
|
|||||||
{name: '20.0.2', commit: ''}
|
{name: '20.0.2', commit: ''}
|
||||||
]
|
]
|
||||||
|
|
||||||
const tagResolver = {
|
const tagResolver: TagResolver = {
|
||||||
method: 'non-existing-method',
|
method: 'non-existing-method',
|
||||||
filter: {
|
filter: {
|
||||||
pattern: 'api-(.+)',
|
pattern: 'api-(.+)',
|
||||||
|
method: 'match',
|
||||||
flags: 'gu'
|
flags: 'gu'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const filtered = filterTags(tags, tagResolver)
|
const filter = validateRegex(tagResolver.filter)
|
||||||
|
const filtered = filterTags(tags, filter)
|
||||||
.map(function (tag) {
|
.map(function (tag) {
|
||||||
return tag.name
|
return tag.name
|
||||||
})
|
})
|
||||||
@@ -131,14 +134,16 @@ it('Should filter tags correctly using the regex (inverse)', async () => {
|
|||||||
{name: '20.0.2', commit: ''}
|
{name: '20.0.2', commit: ''}
|
||||||
]
|
]
|
||||||
|
|
||||||
const tagResolver = {
|
const tagResolver: TagResolver = {
|
||||||
method: 'non-existing-method',
|
method: 'non-existing-method',
|
||||||
filter: {
|
filter: {
|
||||||
pattern: '^(?!\\w+-)(.+)',
|
pattern: '^(?!\\w+-)(.+)',
|
||||||
|
method: 'match',
|
||||||
flags: 'gu'
|
flags: 'gu'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const filtered = filterTags(tags, tagResolver)
|
const filter = validateRegex(tagResolver.filter)
|
||||||
|
const filtered = filterTags(tags, filter)
|
||||||
.map(function (tag) {
|
.map(function (tag) {
|
||||||
return tag.name
|
return tag.name
|
||||||
})
|
})
|
||||||
@@ -160,7 +165,7 @@ it('Should transform tags correctly using the regex', async () => {
|
|||||||
{name: '20.0.2', commit: ''}
|
{name: '20.0.2', commit: ''}
|
||||||
]
|
]
|
||||||
|
|
||||||
const tagResolver = {
|
const tagResolver: TagResolver = {
|
||||||
method: 'non-existing-method',
|
method: 'non-existing-method',
|
||||||
transformer: {
|
transformer: {
|
||||||
pattern: '(api\-)?(.+)',
|
pattern: '(api\-)?(.+)',
|
||||||
@@ -168,7 +173,7 @@ it('Should transform tags correctly using the regex', async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformer = validateTransformer(tagResolver.transformer)
|
const transformer = validateRegex(tagResolver.transformer)
|
||||||
if(transformer != null) {
|
if(transformer != null) {
|
||||||
const transformed = transformTags(tags, transformer)
|
const transformed = transformTags(tags, transformer)
|
||||||
.map(function (tag) {
|
.map(function (tag) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Extractor, PullConfiguration, Regex, Rule, Sort, Transformer} from './pr-collector/types'
|
import {Extractor, PullConfiguration, Regex, Rule, Sort} from './pr-collector/types'
|
||||||
|
|
||||||
export interface Configuration extends PullConfiguration {
|
export interface Configuration extends PullConfiguration {
|
||||||
max_tags_to_fetch: number
|
max_tags_to_fetch: number
|
||||||
@@ -14,7 +14,7 @@ export interface Configuration extends PullConfiguration {
|
|||||||
label_extractor: Extractor[]
|
label_extractor: Extractor[]
|
||||||
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: Transformer[]
|
transformers: Regex[]
|
||||||
tag_resolver: TagResolver
|
tag_resolver: TagResolver
|
||||||
base_branches: string[]
|
base_branches: string[]
|
||||||
custom_placeholders?: Placeholder[]
|
custom_placeholders?: Placeholder[]
|
||||||
@@ -51,13 +51,13 @@ export type Property =
|
|||||||
export interface TagResolver {
|
export interface TagResolver {
|
||||||
method: string // semver, sort
|
method: string // semver, sort
|
||||||
filter?: Regex // the regex to filter the tags, prior to sorting
|
filter?: Regex // the regex to filter the tags, prior to sorting
|
||||||
transformer?: Transformer // transforms the tag name using the regex, run after the filter
|
transformer?: Regex // transforms the tag name using the regex, run after the filter
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Placeholder {
|
export interface Placeholder {
|
||||||
name: string // the name of the new placeholder
|
name: string // the name of the new placeholder
|
||||||
source: string // the src placeholder which will be used to apply the transformer on
|
source: string // the src placeholder which will be used to apply the transformer on
|
||||||
transformer: Transformer // the transformer to use to transform the original placeholder into the custom placheolder
|
transformer: Regex // the transformer to use to transform the original placeholder into the custom placheolder
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultConfiguration: Configuration = {
|
export const DefaultConfiguration: Configuration = {
|
||||||
|
|||||||
+103
-19
@@ -1,25 +1,17 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Extractor, Property, Regex, RegexTransformer, Transformer} from './types'
|
import {Extractor, Property, Regex, RegexTransformer} from './types'
|
||||||
|
|
||||||
export function validateTransformer(transformer?: Regex): RegexTransformer | null {
|
export function validateRegex(regex?: Regex): RegexTransformer | null {
|
||||||
if (transformer === undefined) {
|
if (regex === undefined) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let target = undefined
|
const target = regex.target
|
||||||
if (transformer.hasOwnProperty('target')) {
|
const method = regex.method
|
||||||
target = (transformer as Transformer).target
|
const onEmpty = regex.on_empty
|
||||||
}
|
|
||||||
|
|
||||||
let onProperty = undefined
|
let onProperty = undefined
|
||||||
let method = undefined
|
if (regex.hasOwnProperty('on_property')) {
|
||||||
let onEmpty = undefined
|
onProperty = (regex as Extractor).on_property
|
||||||
if (transformer.hasOwnProperty('method')) {
|
|
||||||
method = (transformer as Extractor).method
|
|
||||||
onEmpty = (transformer as Extractor).on_empty
|
|
||||||
onProperty = (transformer as Extractor).on_property
|
|
||||||
} else if (transformer.hasOwnProperty('on_property')) {
|
|
||||||
onProperty = (transformer as Extractor).on_property
|
|
||||||
}
|
}
|
||||||
// legacy handling, transform single value input to array
|
// legacy handling, transform single value input to array
|
||||||
if (!Array.isArray(onProperty)) {
|
if (!Array.isArray(onProperty)) {
|
||||||
@@ -28,9 +20,9 @@ export function validateTransformer(transformer?: Regex): RegexTransformer | nul
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildRegex(transformer, target, onProperty, method, onEmpty)
|
return buildRegex(regex, target, onProperty, method, onEmpty)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
core.warning(`⚠️ Failed to validate transformer: ${transformer.pattern}`)
|
core.warning(`⚠️ Failed to validate transformer: ${regex.pattern}`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +34,7 @@ export function buildRegex(
|
|||||||
regex: Regex,
|
regex: Regex,
|
||||||
target: string | undefined,
|
target: string | undefined,
|
||||||
onProperty?: Property[] | undefined,
|
onProperty?: Property[] | undefined,
|
||||||
method?: 'replace' | 'match' | undefined,
|
method?: 'replace' | 'replaceAll' | 'match' | 'exec' | 'execAll' | undefined,
|
||||||
onEmpty?: string | undefined
|
onEmpty?: string | undefined
|
||||||
): RegexTransformer | null {
|
): RegexTransformer | null {
|
||||||
try {
|
try {
|
||||||
@@ -58,3 +50,95 @@ export function buildRegex(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
export function applyCaptureGroup(value: RegExpMatchArray, target: string): string | null {
|
||||||
|
const groups = value['groups']
|
||||||
|
if (groups) {
|
||||||
|
const matched = groups[target]
|
||||||
|
if (matched) {
|
||||||
|
// if we had a perfect group match return that.
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.startsWith('$') && !target.startsWith('$$')) {
|
||||||
|
// if we start with $ offer support for matching index based capture groups
|
||||||
|
const index = Number(target.substring(1))
|
||||||
|
if (!isNaN(index) && index < value.length) {
|
||||||
|
return value[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformStringToValues(value: string, extractor: RegexTransformer): string[] | null {
|
||||||
|
if (extractor.pattern == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extractor.method === 'exec' || extractor.method === 'execAll') {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
let matches: RegExpMatchArray | null
|
||||||
|
const result: Set<string> = new Set()
|
||||||
|
// match regex to all occurrences in the string if we run `execAll`
|
||||||
|
// otherwise just do the first match with exec
|
||||||
|
do {
|
||||||
|
matches = extractor.pattern.exec(value)
|
||||||
|
if (matches) {
|
||||||
|
if (extractor.target) {
|
||||||
|
const matchedGroup = applyCaptureGroup(matches, extractor.target)
|
||||||
|
if (matchedGroup) {
|
||||||
|
result.add(matchedGroup)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const match of matches) {
|
||||||
|
result.add(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (matches && extractor.method === 'execAll')
|
||||||
|
if (result.size > 0) {
|
||||||
|
return [...result]
|
||||||
|
}
|
||||||
|
} else if (extractor.method === 'match') {
|
||||||
|
const matches = value.match(extractor.pattern)
|
||||||
|
if (matches !== null && matches.length > 0) {
|
||||||
|
if (extractor.target) {
|
||||||
|
const matchedGroup = applyCaptureGroup(matches, extractor.target)
|
||||||
|
if (matchedGroup) {
|
||||||
|
return [matchedGroup]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches.map(match => match || '')
|
||||||
|
}
|
||||||
|
} else if (extractor.method === 'replaceAll') {
|
||||||
|
const match = value.replaceAll(extractor.pattern, extractor.target)
|
||||||
|
if (match !== '') {
|
||||||
|
return [match]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const match = value.replace(extractor.pattern, extractor.target)
|
||||||
|
if (match !== '') {
|
||||||
|
return [match]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extractor.onEmpty !== undefined) {
|
||||||
|
return [extractor.onEmpty]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformStringToOptionalValue(value: string, extractor: RegexTransformer): string | null {
|
||||||
|
const result = transformStringToValues(value, extractor)
|
||||||
|
if (result != null && result.length > 0) {
|
||||||
|
return result[0]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformStringToValue(value: string, extractor: RegexTransformer): string {
|
||||||
|
return transformStringToOptionalValue(value, extractor) || ''
|
||||||
|
}
|
||||||
|
|||||||
+11
-11
@@ -2,10 +2,10 @@ import * as core from '@actions/core'
|
|||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as semver from 'semver'
|
import * as semver from 'semver'
|
||||||
import {SemVer} from 'semver'
|
import {SemVer} from 'semver'
|
||||||
import {RegexTransformer, TagResolver, Transformer} from './types'
|
import {Regex, RegexTransformer, TagResolver} from './types'
|
||||||
import {createCommandManager} from './gitHelper'
|
import {createCommandManager} from './gitHelper'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import {validateTransformer} from './regexUtils'
|
import {transformStringToOptionalValue, transformStringToValue, validateRegex} from './regexUtils'
|
||||||
import {BaseRepository} from '../repositories/BaseRepository'
|
import {BaseRepository} from '../repositories/BaseRepository'
|
||||||
|
|
||||||
export interface TagResult {
|
export interface TagResult {
|
||||||
@@ -88,15 +88,17 @@ export class Tags {
|
|||||||
let tags: TagInfo[] = []
|
let tags: TagInfo[] = []
|
||||||
|
|
||||||
if (!toTag || !fromTag) {
|
if (!toTag || !fromTag) {
|
||||||
|
const filterRegex = validateRegex(tagResolver.filter)
|
||||||
|
|
||||||
// filter out tags not matching the specified filter
|
// filter out tags not matching the specified filter
|
||||||
const filteredTags = filterTags(
|
const filteredTags = filterTags(
|
||||||
// retrieve the tags from the API
|
// retrieve the tags from the API
|
||||||
await this.getTags(owner, repo, maxTagsToFetch),
|
await this.getTags(owner, repo, maxTagsToFetch),
|
||||||
tagResolver
|
filterRegex
|
||||||
)
|
)
|
||||||
|
|
||||||
// check if a transformer, legacy handling, transform single value input to array
|
// check if a transformer, legacy handling, transform single value input to array
|
||||||
let tagTransfomers: Transformer[] | undefined = undefined
|
let tagTransfomers: Regex[] | undefined = undefined
|
||||||
if (tagResolver.transformer !== undefined) {
|
if (tagResolver.transformer !== undefined) {
|
||||||
if (!Array.isArray(tagResolver.transformer)) {
|
if (!Array.isArray(tagResolver.transformer)) {
|
||||||
tagTransfomers = [tagResolver.transformer]
|
tagTransfomers = [tagResolver.transformer]
|
||||||
@@ -109,7 +111,7 @@ export class Tags {
|
|||||||
let transformedTags: TagInfo[] = filteredTags
|
let transformedTags: TagInfo[] = filteredTags
|
||||||
if (tagTransfomers !== undefined && tagTransfomers.length > 0) {
|
if (tagTransfomers !== undefined && tagTransfomers.length > 0) {
|
||||||
for (const transformer of tagTransfomers) {
|
for (const transformer of tagTransfomers) {
|
||||||
const tagTransformer = validateTransformer(transformer)
|
const tagTransformer = validateRegex(transformer)
|
||||||
if (tagTransformer != null) {
|
if (tagTransformer != null) {
|
||||||
core.debug(`ℹ️ Using configured tagTransformer (${transformer.pattern})`)
|
core.debug(`ℹ️ Using configured tagTransformer (${transformer.pattern})`)
|
||||||
transformedTags = transformTags(transformedTags, tagTransformer)
|
transformedTags = transformTags(transformedTags, tagTransformer)
|
||||||
@@ -196,11 +198,9 @@ export class Tags {
|
|||||||
* Uses the provided filter (if available) to filter out any tags not currently relevant.
|
* Uses the provided filter (if available) to filter out any tags not currently relevant.
|
||||||
* https://github.com/mikepenz/release-changelog-builder-action/issues/566
|
* https://github.com/mikepenz/release-changelog-builder-action/issues/566
|
||||||
*/
|
*/
|
||||||
export function filterTags(tags: TagInfo[], tagResolver: TagResolver): TagInfo[] {
|
export function filterTags(tags: TagInfo[], filterRegex: RegexTransformer | null): TagInfo[] {
|
||||||
const filter = tagResolver.filter
|
if (filterRegex !== null) {
|
||||||
if (filter !== undefined) {
|
const filteredTags = tags.filter(tag => transformStringToOptionalValue(tag.name, filterRegex) !== null)
|
||||||
const regex = new RegExp(filter.pattern.replace('\\\\', '\\'), filter.flags ?? 'gu')
|
|
||||||
const filteredTags = tags.filter(tag => tag.name.match(regex) !== null)
|
|
||||||
core.debug(`ℹ️ Filtered tags count: ${filteredTags.length}, original count: ${tags.length}`)
|
core.debug(`ℹ️ Filtered tags count: ${filteredTags.length}, original count: ${tags.length}`)
|
||||||
return filteredTags
|
return filteredTags
|
||||||
} else {
|
} else {
|
||||||
@@ -214,7 +214,7 @@ export function filterTags(tags: TagInfo[], tagResolver: TagResolver): TagInfo[]
|
|||||||
export function transformTags(tags: TagInfo[], transformer: RegexTransformer): TagInfo[] {
|
export function transformTags(tags: TagInfo[], transformer: RegexTransformer): TagInfo[] {
|
||||||
return tags.map(function (tag) {
|
return tags.map(function (tag) {
|
||||||
if (transformer.pattern) {
|
if (transformer.pattern) {
|
||||||
const transformedName = tag.name.replace(transformer.pattern, transformer.target)
|
const transformedName = transformStringToValue(tag.name, transformer)
|
||||||
core.debug(`ℹ️ Transformed ${tag.name} to ${transformedName}`)
|
core.debug(`ℹ️ Transformed ${tag.name} to ${transformedName}`)
|
||||||
return {
|
return {
|
||||||
tmp: tag.name, // remember the original name
|
tmp: tag.name, // remember the original name
|
||||||
|
|||||||
@@ -36,28 +36,25 @@ export interface Sort {
|
|||||||
export interface TagResolver {
|
export interface TagResolver {
|
||||||
method: string // semver, sort
|
method: string // semver, sort
|
||||||
filter?: Regex // the regex to filter the tags, prior to sorting
|
filter?: Regex // the regex to filter the tags, prior to sorting
|
||||||
transformer?: Transformer | Transformer[] // transforms the tag name using the regex, run after the filter
|
transformer?: Regex | Regex[] // transforms the tag name using the regex, run after the filter
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Regex {
|
export interface Regex {
|
||||||
pattern: string // the regex pattern to match
|
pattern: string // the regex pattern to match
|
||||||
flags?: string // the regex flag to use for RegExp
|
flags?: string // the regex flag to use for RegExp
|
||||||
}
|
|
||||||
|
|
||||||
export interface Transformer extends Regex {
|
|
||||||
target?: string // the target string to transform the source string using the regex to
|
target?: string // the target string to transform the source string using the regex to
|
||||||
|
method?: 'replace' | 'replaceAll' | 'match' | 'exec' | 'execAll' | 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Extractor extends Transformer {
|
export interface Extractor extends Regex {
|
||||||
on_property?: Property[] | Property | undefined // retrieve the property to extract the value from
|
on_property?: Property[] | Property | 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegexTransformer {
|
export interface RegexTransformer {
|
||||||
pattern: RegExp | null
|
pattern: RegExp | null
|
||||||
target: string
|
target: string
|
||||||
onProperty?: Property[]
|
onProperty?: Property[]
|
||||||
method?: 'replace' | 'match'
|
method?: 'replace' | 'replaceAll' | 'match' | 'exec' | 'execAll'
|
||||||
onEmpty?: string
|
onEmpty?: string
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,13 +1,13 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {RegexTransformer, Rule} from './pr-collector/types'
|
import {RegexTransformer, Rule} from './pr-collector/types'
|
||||||
import {PullRequestInfo, retrieveProperty} from './pr-collector/pullRequests'
|
import {PullRequestInfo, retrieveProperty} from './pr-collector/pullRequests'
|
||||||
import {validateTransformer} from './pr-collector/regexUtils'
|
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 => validateTransformer(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 => {
|
||||||
return matches(pr, transformer, 'rule')
|
return matches(pr, transformer, 'rule')
|
||||||
|
|||||||
+15
-24
@@ -10,8 +10,8 @@ import {
|
|||||||
sortPullRequests
|
sortPullRequests
|
||||||
} from './pr-collector/pullRequests'
|
} from './pr-collector/pullRequests'
|
||||||
import {DiffInfo} from './pr-collector/commits'
|
import {DiffInfo} from './pr-collector/commits'
|
||||||
import {validateTransformer} from './pr-collector/regexUtils'
|
import {transformStringToOptionalValue, transformStringToValues, validateRegex} from './pr-collector/regexUtils'
|
||||||
import {RegexTransformer, Transformer} from './pr-collector/types'
|
import {Regex, RegexTransformer} from './pr-collector/types'
|
||||||
import {ReleaseNotesOptions} from './releaseNotesBuilder'
|
import {ReleaseNotesOptions} from './releaseNotesBuilder'
|
||||||
import {matchesRules} from './regexUtils'
|
import {matchesRules} from './regexUtils'
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
|
|
||||||
// establish parent child PR relations
|
// establish parent child PR relations
|
||||||
if (config.reference !== undefined) {
|
if (config.reference !== undefined) {
|
||||||
const reference = validateTransformer(config.reference)
|
const reference = validateRegex(config.reference)
|
||||||
if (reference !== null) {
|
if (reference !== null) {
|
||||||
core.info(`ℹ️ Identifying PR references using \`reference\``)
|
core.info(`ℹ️ Identifying PR references using \`reference\``)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
|
|||||||
|
|
||||||
// drop duplicate pull requests
|
// drop duplicate pull requests
|
||||||
if (config.duplicate_filter !== undefined) {
|
if (config.duplicate_filter !== undefined) {
|
||||||
const extractor = validateTransformer(config.duplicate_filter)
|
const extractor = validateRegex(config.duplicate_filter)
|
||||||
if (extractor !== null) {
|
if (extractor !== null) {
|
||||||
core.info(`ℹ️ Remove duplicated pull requests using \`duplicate_filter\``)
|
core.info(`ℹ️ Remove duplicated pull requests using \`duplicate_filter\``)
|
||||||
|
|
||||||
@@ -461,11 +461,12 @@ function handlePlaceholder(
|
|||||||
const phs = placeholders.get(key)
|
const phs = placeholders.get(key)
|
||||||
if (phs) {
|
if (phs) {
|
||||||
for (const placeholder of phs) {
|
for (const placeholder of phs) {
|
||||||
const transformer = validateTransformer(placeholder.transformer)
|
const transformer = validateRegex(placeholder.transformer)
|
||||||
if (transformer?.pattern) {
|
if (transformer?.pattern) {
|
||||||
const extractedValue = value.replace(transformer.pattern, transformer.target)
|
const extractedValue = transformStringToOptionalValue(value, transformer)
|
||||||
// note: `.replace` will return the full string again if there was no match
|
// note: `.replace` will return the full string again if there was no match
|
||||||
if (extractedValue && (extractedValue !== value || (extractedValue === value && value.match(transformer.pattern)))) {
|
// note: This is mostly backwards compatiblity
|
||||||
|
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
|
||||||
if (placeholderPrMap) {
|
if (placeholderPrMap) {
|
||||||
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
|
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
|
||||||
}
|
}
|
||||||
@@ -475,7 +476,7 @@ function handlePlaceholder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
core.debug(` Custom Placeholder successfully matched data - ${extractValues} (${placeholder.name})`)
|
core.debug(` Custom Placeholder successfully matched data - ${extractedValue} (${placeholder.name})`)
|
||||||
}
|
}
|
||||||
} else if (core.isDebug() && extractedValue === value) {
|
} else if (core.isDebug() && extractedValue === value) {
|
||||||
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`)
|
core.debug(` Custom Placeholder did result in the full original value returned. Skipping. (${placeholder.name})`)
|
||||||
@@ -580,11 +581,11 @@ function transform(filled: string, transformers: RegexTransformer[]): string {
|
|||||||
return transformed
|
return transformed
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateTransformers(specifiedTransformers: Transformer[]): RegexTransformer[] {
|
function validateTransformers(specifiedTransformers: Regex[]): RegexTransformer[] {
|
||||||
const transformers = specifiedTransformers
|
const transformers = specifiedTransformers
|
||||||
return transformers
|
return transformers
|
||||||
.map(transformer => {
|
.map(transformer => {
|
||||||
return validateTransformer(transformer)
|
return validateRegex(transformer)
|
||||||
})
|
})
|
||||||
.filter(transformer => transformer?.pattern != null)
|
.filter(transformer => transformer?.pattern != null)
|
||||||
.map(transformer => {
|
.map(transformer => {
|
||||||
@@ -619,20 +620,10 @@ function extractValuesFromString(value: string, extractor: RegexTransformer): st
|
|||||||
if (extractor.pattern == null) {
|
if (extractor.pattern == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
const transformed = transformStringToValues(value, extractor)
|
||||||
if (extractor.method === 'match') {
|
if (transformed) {
|
||||||
const lables = value.match(extractor.pattern)
|
return transformed.map(val => val?.toLocaleLowerCase('en') || '')
|
||||||
if (lables !== null && lables.length > 0) {
|
|
||||||
return lables.map(label => label?.toLocaleLowerCase('en') || '')
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const label = value.replace(extractor.pattern, extractor.target)
|
return null
|
||||||
if (label !== '') {
|
|
||||||
return [label.toLocaleLowerCase('en')]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (extractor.onEmpty !== undefined) {
|
|
||||||
return [extractor.onEmpty.toLocaleLowerCase('en')]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user