Skip to content

Commit aaef604

Browse files
committed
refactor(rules:if): remove dead _evaluateRuleIf and simplify _nodeToAtom
- Delete _evaluateRuleIf: its regex-substitution logic (pattern1/pattern2, null.matchRE2JS replacements, re-expansion) was entirely unreachable when called from the jsep walk, which handles =~/!~/&&/|| before falling through - Inline a direct eval in the walk fallback (for ==, !=, comparisons, bare literals), replacing the _evaluateRuleIf call - Drop unused envs parameter from _nodeToAtom and remove commented-out block
1 parent 5b26451 commit aaef604

1 file changed

Lines changed: 23 additions & 123 deletions

File tree

src/utils.ts

Lines changed: 23 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {AxiosRequestConfig} from "axios";
1818
import path from "node:path";
1919
import {Argv} from "./argv.js";
2020

21-
jsep.plugins.register(jsepRegex); // /pattern/flags literals
21+
jsep.plugins.register(jsepRegex); // /pattern/flags literals
2222
jsep.addBinaryOp("=~", 10); // regex match operator
2323
jsep.addBinaryOp("!~", 10); // regex non-match operator
2424

@@ -222,20 +222,13 @@ export class Utils {
222222
// Reconstruct the source string for an atomic (non-logical) jsep node.
223223
// Identifiers keep their name ($VAR), literals use their raw form so that
224224
// strings retain quotes and regexes retain slashes and flags.
225-
private static _nodeToAtom (node: jsep.Expression, envs: {[key: string]: string}): string {
225+
private static _nodeToAtom (node: jsep.Expression): string {
226226
switch (node.type) {
227-
case "Identifier": {
228-
const n = node as jsep.Identifier;
229-
return n.name;
230-
// return this.expandTextWith(n.name, {
231-
// unescape: JSON.stringify("$"),
232-
// variable: (name) => JSON.stringify(envs[name] ?? null).replaceAll("\\\\", "\\"),
233-
// });
234-
}
227+
case "Identifier": return (node as jsep.Identifier).name;
235228
case "Literal": return (node as jsep.Literal).raw;
236229
case "BinaryExpression": {
237230
const n = node as jsep.BinaryExpression;
238-
return `${Utils._nodeToAtom(n.left, envs)} ${n.operator} ${Utils._nodeToAtom(n.right, envs)}`;
231+
return `${Utils._nodeToAtom(n.left)} ${n.operator} ${Utils._nodeToAtom(n.right)}`;
239232
}
240233
default: return "";
241234
}
@@ -317,7 +310,7 @@ export class Utils {
317310
const assertMsg = [
318311
"Error attempting to evaluate the following rules:",
319312
" rules:",
320-
` - if: '${Utils._nodeToAtom(node, envs)}'`,
313+
` - if: '${Utils._nodeToAtom(node)}'`,
321314
"as",
322315
"```javascript",
323316
`${evalStr}`,
@@ -328,10 +321,24 @@ export class Utils {
328321
return Boolean(res);
329322
}
330323
}
331-
// assert (node.type === "Literal", `not a Literal: ${JSON.stringify(node)}`)
332-
const res = Utils._evaluateRuleIf(Utils._nodeToAtom(node, envs), envs);
333-
// console.log(`${JSON.stringify(node)} -> ${res}`)
334-
return res;
324+
const atom = Utils._nodeToAtom(node);
325+
let res;
326+
try {
327+
(globalThis as any).RE2JS = RE2JS;
328+
res = (0, eval)(atom);
329+
delete (globalThis as any).RE2JS;
330+
} catch {
331+
assert(false, [
332+
"Error attempting to evaluate the following rules:",
333+
" rules:",
334+
` - if: '${ruleIf}'`,
335+
"as",
336+
"```javascript",
337+
`${atom}`,
338+
"```",
339+
].join("\n"));
340+
}
341+
return Boolean(res);
335342
};
336343

337344
let ast;
@@ -352,113 +359,6 @@ export class Utils {
352359
return walk(ast!);
353360
}
354361

355-
static _evaluateRuleIf (ruleIf: string | undefined, envs: {[key: string]: string}): boolean {
356-
if (ruleIf === undefined) return true;
357-
assert(!/\$\{\w+\}/.test(ruleIf), chalk`rules:rule if invalid expression syntax: {blueBright ${ruleIf}}\nuse {green $VAR} not {red \${VAR\}} in rules:if`);
358-
let evalStr = ruleIf;
359-
360-
const flagsToBinary = (flags: string): number => {
361-
let binary = 0;
362-
if (flags.includes("i")) {
363-
binary |= RE2JS.CASE_INSENSITIVE;
364-
}
365-
if (flags.includes("s")) {
366-
binary |= RE2JS.DOTALL;
367-
}
368-
if (flags.includes("m")) {
369-
binary |= RE2JS.MULTILINE;
370-
}
371-
return binary;
372-
};
373-
374-
evalStr = this.expandTextWith(evalStr, {
375-
unescape: JSON.stringify("$"),
376-
variable: (name) => JSON.stringify(envs[name] ?? null).replaceAll("\\\\", "\\"),
377-
});
378-
const expandedEvalStr = evalStr;
379-
380-
// Scenario when RHS is a <regex>
381-
// https://regexr.com/85sjo
382-
const pattern1 = /\s*(?<operator>(?:=~)|(?:!~))\s*\/(?<rhs>.*?[^\\])\/(?<flags>[igmsuy]*)(\s|$|\))/g;
383-
evalStr = evalStr.replaceAll(pattern1, (_, operator, rhs, flags, remainingTokens) => {
384-
let _operator;
385-
switch (operator) {
386-
case "=~":
387-
_operator = "!="; // Matches are found (!= null)
388-
break;
389-
case "!~":
390-
_operator = "=="; // Matches are not found (== null)
391-
break;
392-
default:
393-
throw operator;
394-
}
395-
const _rhs = JSON.stringify(rhs); // JSON.stringify for escaping `"`
396-
const containsNonEscapedSlash = /(?<!\\)\//.test(_rhs);
397-
const assertMsg = [
398-
"Error attempting to evaluate the following rules:",
399-
" rules:",
400-
` - if: '${expandedEvalStr}'`,
401-
"as rhs contains unescaped quote",
402-
];
403-
assert(!containsNonEscapedSlash, assertMsg.join("\n"));
404-
const flagsBinary = flagsToBinary(flags);
405-
return `.matchRE2JS(RE2JS.compile(${_rhs}, ${flagsBinary})) ${_operator} null${remainingTokens}`;
406-
});
407-
408-
// Scenario when RHS is surrounded by single/double-quotes
409-
// https://regexr.com/85t0g
410-
const pattern2 = /\s*(?<operator>=~|!~)\s*(["'])(?<rhs>(?:\\.|[^\\])*?)\2/g;
411-
evalStr = evalStr.replaceAll(pattern2, (_, operator, __, rhs) => {
412-
let _operator;
413-
switch (operator) {
414-
case "=~":
415-
_operator = "!="; // Matches are found (!= null)
416-
break;
417-
case "!~":
418-
_operator = "=="; // Matches are not found (== null)
419-
break;
420-
default:
421-
throw operator;
422-
}
423-
424-
const assertMsg = [
425-
"RHS (${rhs}) must be a regex pattern. Do not rely on this behavior!",
426-
"Refer to https://docs.gitlab.com/ee/ci/jobs/job_rules.html#unexpected-behavior-from-regular-expression-matching-with- for more info...",
427-
];
428-
assert((/\/(.*)\/(\w*)/.test(rhs)), assertMsg.join("\n"));
429-
430-
const regex = /\/(?<pattern>.*)\/(?<flags>[igmsuy]*)/;
431-
const _rhs = rhs.replace(regex, (_: string, pattern: string, flags: string) => {
432-
const flagsBinary = flagsToBinary(flags);
433-
return `RE2JS.compile("${pattern}", ${flagsBinary})`;
434-
});
435-
return `.matchRE2JS(${_rhs}) ${_operator} null`;
436-
});
437-
438-
evalStr = evalStr.replaceAll(/null.matchRE2JS\(.+?\)\s*!=\s*null/g, "false");
439-
evalStr = evalStr.replaceAll(/null.matchRE2JS\(.+?\)\s*==\s*null/g, "true");
440-
441-
evalStr = evalStr.trim();
442-
443-
let res;
444-
try {
445-
(globalThis as any).RE2JS = RE2JS;
446-
res = (0, eval)(evalStr); // indirect eval
447-
delete (globalThis as any).RE2JS;
448-
} catch {
449-
const assertMsg = [
450-
"Error attempting to evaluate the following rules:",
451-
" rules:",
452-
` - if: '${expandedEvalStr}'`,
453-
"as",
454-
"```javascript",
455-
`${evalStr}`,
456-
"```",
457-
];
458-
assert(false, assertMsg.join("\n"));
459-
}
460-
return Boolean(res);
461-
}
462362

463363
static evaluateRuleExist (cwd: string, ruleExists: string[] | {paths: string[]} | undefined): boolean {
464364
if (ruleExists === undefined) return true;

0 commit comments

Comments
 (0)