@@ -101,11 +101,7 @@ export class Utils {
101
101
} else {
102
102
const name = var1 || var2 ;
103
103
assert ( name , "unexpected unset capture group" ) ;
104
- let value = `${ expandWith . variable ( name ) } ` ;
105
- if ( value . startsWith ( "\"/" ) && value . endsWith ( "/\"" ) ) {
106
- value = value . substring ( 1 ) . slice ( 0 , - 1 ) ;
107
- }
108
- return `${ value } ` ;
104
+ return `${ expandWith . variable ( name ) } ` ;
109
105
}
110
106
}
111
107
) ;
@@ -205,20 +201,74 @@ export class Utils {
205
201
unescape : JSON . stringify ( "$" ) ,
206
202
variable : ( name ) => JSON . stringify ( envs [ name ] ?? null ) . replaceAll ( "\\\\" , "\\" ) ,
207
203
} ) ;
204
+ const expandedEvalStr = evalStr ;
205
+
206
+ // Scenario when RHS is a <regex>
207
+ // https://regexr.com/85sjo
208
+ const pattern1 = / \s * (?< operator > (?: = ~ ) | (?: ! ~ ) ) \s * (?< rhs > \/ .* ?\/ ) (?< flags > [ i g m s u y ] * ) ( \s | $ | \) ) / g;
209
+ evalStr = evalStr . replace ( pattern1 , ( _ , operator , rhs , flags , remainingTokens ) => {
210
+ let _operator ;
211
+ switch ( operator ) {
212
+ case "=~" :
213
+ _operator = "!=" ;
214
+ break ;
215
+ case "!~" :
216
+ _operator = "==" ;
217
+ break ;
218
+ default :
219
+ throw operator ;
220
+ }
221
+ return `.match(${ rhs } ${ flags } )${ remainingTokens } ${ _operator } null` ;
222
+ } ) ;
208
223
209
- // Convert =~ to match function
210
- evalStr = evalStr . replace ( / \s * = ~ \s * ( \/ .* ?\/ [ i g m s u y ] * ) (?: \s | $ ) / g, ".match($1) != null" ) ;
211
- evalStr = evalStr . replace ( / \s * = ~ \s ( .+ ?) ( \) * ?) (?: \s | $ ) / g, ".match(new RegExp($1)) != null$2" ) ; // Without forward slashes
224
+ // Scenario when RHS is surrounded by double-quotes
225
+ // https://regexr.com/85t0g
226
+ const pattern2 = / \s * (?< operator > (?: = ~ ) | (?: ! ~ ) ) \s * " (?< rhs > [ ^ " \\ ] * (?: \\ .[ ^ " \\ ] * ) * ) " / g;
227
+ evalStr = evalStr . replace ( pattern2 , ( _ , operator , rhs ) => {
228
+ let _operator ;
229
+ switch ( operator ) {
230
+ case "=~" :
231
+ _operator = "!=" ;
232
+ break ;
233
+ case "!~" :
234
+ _operator = "==" ;
235
+ break ;
236
+ default :
237
+ throw operator ;
238
+ }
212
239
213
- // Convert !~ to match function
214
- evalStr = evalStr . replace ( / \s * ! ~ \s * ( \/ .* ?\/ [ i g m s u y ] * ) (?: \s | $ ) / g, ".match($1) == null" ) ;
215
- evalStr = evalStr . replace ( / \s * ! ~ \s ( .+ ?) ( \) * ?) (?: \s | $ ) / g, ".match(new RegExp($1)) == null$2" ) ; // Without forward slashes
240
+ if ( ! / \/ ( .* ) \/ ( [ \w ] * ) / . test ( rhs ) ) {
241
+ throw Error ( `RHS (${ rhs } ) must be a regex pattern. Do not rely on this behavior!
242
+ Refer to https://docs.gitlab.com/ee/ci/jobs/job_rules.html#unexpected-behavior-from-regular-expression-matching-with- for more info...` ) ;
243
+ }
244
+ const regex = / \/ (?< pattern > .* ) \/ (?< flags > [ i g m s u y ] * ) / ;
245
+ const _rhs = rhs . replace ( regex , ( _ : string , pattern : string , flags : string ) => {
246
+ const _pattern = pattern . replace ( / (?< ! \\ ) \/ / g, "\\/" ) ; // escape potentially unescaped `/` that's in the pattern
247
+ return `/${ _pattern } /${ flags } ` ;
248
+ } ) ;
249
+ return `.match(new RegExp(${ _rhs } )) ${ _operator } null` ;
250
+ } ) ;
216
251
217
252
// Convert all null.match functions to false
218
253
evalStr = evalStr . replace ( / n u l l .m a t c h \( .+ ?\) ! = n u l l / g, "false" ) ;
219
254
evalStr = evalStr . replace ( / n u l l .m a t c h \( .+ ?\) = = n u l l / g, "false" ) ;
220
255
221
- return Boolean ( eval ( evalStr ) ) ;
256
+ let res ;
257
+ try {
258
+ res = eval ( evalStr ) ;
259
+ } catch ( err ) {
260
+ console . log ( `
261
+ Error attempting to evaluate the following rules:
262
+ rules:
263
+ - if: '${ expandedEvalStr } '
264
+ as
265
+ \`\`\`javascript
266
+ ${ evalStr }
267
+ \`\`\`
268
+ ` ) ;
269
+ throw err ;
270
+ }
271
+ return Boolean ( res ) ;
222
272
}
223
273
224
274
static evaluateRuleExist ( cwd : string , ruleExists : string [ ] | undefined ) : boolean {
0 commit comments