@@ -7,6 +7,8 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '.
77const configPath = path . join ( root , 'release-please-config.json' ) ;
88const manifestPath = path . join ( root , '.release-please-manifest.json' ) ;
99const decoder = new TextDecoder ( ) ;
10+ const RELEASE_PR_TITLE_PATTERN = 'chore${scope}: release${component} ${version}' ;
11+ const GROUP_RELEASE_PR_TITLE_PATTERN = 'chore(release): prepare ${branch} releases' ;
1012
1113function fail ( message ) {
1214 console . error ( `check_release_please_config.mjs: ${ message } ` ) ;
@@ -259,6 +261,39 @@ function validatePlugins(plugins, pathsById) {
259261 ) ;
260262 }
261263}
264+
265+ function releasePleaseTitlePatternRegex ( pattern ) {
266+ if ( typeof pattern !== 'string' || ! pattern ) {
267+ fail ( `release-please title pattern must be a non-empty string: ${ JSON . stringify ( pattern ) } ` ) ;
268+ }
269+ return new RegExp (
270+ `^${ pattern
271+ . replace ( '[' , '\\[' )
272+ . replace ( ']' , '\\]' )
273+ . replace ( '(' , '\\(' )
274+ . replace ( ')' , '\\)' )
275+ . replace ( '${scope}' , '(\\((?<branch>[\\w-./]+)\\))?' )
276+ . replace ( '${component}' , ' ?(?<component>@?[\\w-./]*)?' )
277+ . replace ( '${version}' , 'v?(?<version>[0-9].*)' )
278+ . replace ( '${branch}' , '(?<branch>[\\w-./]+)?' ) } $`,
279+ ) ;
280+ }
281+
282+ function assertParseableReleasePleaseTitle ( pattern , title , context ) {
283+ const match = title . match ( releasePleaseTitlePatternRegex ( pattern ) ) ;
284+ if ( ! match ?. groups ) {
285+ fail ( `${ context } must be parseable by release-please: ${ JSON . stringify ( title ) } ` ) ;
286+ }
287+ }
288+
289+ function renderReleasePleaseTitle ( pattern , { targetBranch } ) {
290+ return pattern
291+ . replace ( '${scope}' , targetBranch ? `(${ targetBranch } )` : '' )
292+ . replace ( '${component}' , '' )
293+ . replace ( '${version}' , '' )
294+ . replace ( '${branch}' , targetBranch ?? '' )
295+ . trim ( ) ;
296+ }
262297if ( actualPaths . size !== expectedPaths . size || sortedDifference ( expectedPaths , actualPaths ) . length > 0 ) {
263298 fail (
264299 `release-please packages must match release products:\nmissing=${ JSON . stringify ( sortedDifference ( expectedPaths , actualPaths ) ) } \nextra=${ JSON . stringify ( sortedDifference ( actualPaths , expectedPaths ) ) } ` ,
@@ -276,9 +311,26 @@ if (config['tag-separator'] !== '-') {
276311if ( config [ 'include-v-in-tag' ] !== true ) {
277312 fail ( 'release-please must include v in tags' ) ;
278313}
279- if ( config [ 'pull-request-title-pattern' ] !== 'chore${scope}: release${component} ${version}' ) {
314+ if ( config [ 'pull-request-title-pattern' ] !== RELEASE_PR_TITLE_PATTERN ) {
280315 fail ( "release-please pull-request-title-pattern must keep release-please's parseable default shape" ) ;
281316}
317+ if ( config [ 'group-pull-request-title-pattern' ] !== GROUP_RELEASE_PR_TITLE_PATTERN ) {
318+ fail ( 'release-please group-pull-request-title-pattern must keep grouped release PRs parseable' ) ;
319+ }
320+ const generatedGroupTitle = renderReleasePleaseTitle ( GROUP_RELEASE_PR_TITLE_PATTERN , { targetBranch : 'main' } ) ;
321+ if ( generatedGroupTitle !== 'chore(release): prepare main releases' ) {
322+ fail ( `release-please grouped release PR title rendered unexpectedly: ${ JSON . stringify ( generatedGroupTitle ) } ` ) ;
323+ }
324+ assertParseableReleasePleaseTitle (
325+ GROUP_RELEASE_PR_TITLE_PATTERN ,
326+ generatedGroupTitle ,
327+ 'generated grouped release PR title' ,
328+ ) ;
329+ assertParseableReleasePleaseTitle (
330+ GROUP_RELEASE_PR_TITLE_PATTERN ,
331+ 'chore(release): prepare product releases' ,
332+ 'already-merged grouped release PR #80 title' ,
333+ ) ;
282334if ( config [ 'initial-version' ] !== '0.1.0' ) {
283335 fail ( 'release-please initial-version must bootstrap the first generated release PR to 0.1.0' ) ;
284336}
0 commit comments