@nitra/cursor 1.8.69 → 1.8.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mdc/abie.mdc CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Правила для проєктів AbInBev Efes
3
3
  alwaysApply: true
4
- version: '1.2'
4
+ version: '1.4'
5
5
  ---
6
6
 
7
7
  Правило **abie** для споживачів **@nitra/cursor**: **k8s** (**Deployment** + **HealthCheckPolicy**), overlay **ua** / **ru** (**nodeSelector**, видалення **HealthCheckPolicy** у **ru**) і гілки в **clean-merged-branch**. **`npx @nitra/cursor check abie`** виконується лише якщо в **`.n-cursor.json`** у **`rules`** є **`abie`** — інакше вихід **0** без зауважень.
@@ -30,6 +30,49 @@ spec:
30
30
  name: СЕРВІС
31
31
  ```
32
32
 
33
+ ## k8s: overlay **HTTPRoute** `nginx-run` (**ua** / **ru**)
34
+
35
+ Якщо під **`k8s`** є **Deployment**, у **кожному** **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`** має бути inline **JSON6902** у **`patches[].patch`** на **`target.kind: HTTPRoute`**, **`name: nginx-run`**: **replace** **`/spec/hostnames`** (допустимі домени — як у прикладах нижче) і **replace** **`/spec/parentRefs/0/namespace`** (**`ua`** або **`ru`**). Для **ru** додатково потрібна анотація **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`** (зазвичай **op: add**, **`/metadata/annotations`**). Критерії — **`validateAbieNginxRunHttpRoutePatches`** / **`getCombinedNginxRunPatchTextFromKustomization`** у **`npm/scripts/check-abie.mjs`**.
36
+
37
+ ```yaml title="…/ua/kustomization.yaml (фрагмент)"
38
+ - target:
39
+ kind: HTTPRoute
40
+ name: nginx-run
41
+ patch: |-
42
+ - op: replace
43
+ path: /spec/hostnames
44
+ value:
45
+ - "abie.app" # також допускається vybeerai.com.ua, *.vybeerai.com.ua, *.abie.app
46
+ - op: replace
47
+ path: /spec/parentRefs/0/namespace
48
+ value: ua
49
+ ```
50
+
51
+ ```yaml title="…/ru/kustomization.yaml (фрагмент)"
52
+ - target:
53
+ kind: HTTPRoute
54
+ name: nginx-run
55
+ patch: |-
56
+ - op: replace
57
+ path: /spec/hostnames
58
+ value:
59
+ - "napitkivmeste.tech" # також допускається выбирайонлайн.рф, *.napitkivmeste.tech, *.выбирайонлайн.рф
60
+ - op: replace
61
+ path: /spec/parentRefs/0/namespace
62
+ value: ru
63
+ ```
64
+
65
+ ```yaml title="…/ru/kustomization.yaml (фрагмент)"
66
+ - target:
67
+ kind: HTTPRoute
68
+ name: nginx-run
69
+ patch: |-
70
+ - op: add
71
+ path: /metadata/annotations
72
+ value:
73
+ gwin.yandex.cloud/rules.http.upgradeTypes: "websocket"
74
+ ```
75
+
33
76
  ## k8s: overlay **`ru`** і HealthCheckPolicy
34
77
 
35
78
  Якщо в дереві **k8s** є **HealthCheckPolicy**, **check abie** вимагає **`ru/kustomization.yaml`** з patch **`$patch: delete`** для політики (узгоджено з **k8s.mdc** / **check-k8s**, **`ruKustomizationHasHealthCheckDeletePatch`** у **`npm/scripts/check-k8s.mjs`**). Підстав реальне ім’я замість **`СЕРВІС`**:
@@ -74,6 +117,18 @@ patches:
74
117
  yandex.cloud/preemptible: "false"
75
118
  ```
76
119
 
120
+ ### Базовий Deployment (`…/base/`)
121
+
122
+ Якщо **Deployment** у YAML під **`k8s`** лежить у шляху з сегментом **`base`** (наприклад **`…/k8s/base/deploy.yaml`**), у **`spec.template.spec.nodeSelector`** має бути **`preem`** зі значенням **істинно** (**`true`** або рядок **`'true'`**) — overlay **ua** / **ru** підміняє селектор через kustomize. Деталі — **`deploymentDocumentHasAbieBasePreemNodeSelector`** / **`isAbieK8sBaseYamlPath`** у **`npm/scripts/check-abie.mjs`**.
123
+
124
+ ```yaml title="…/base/deploy.yaml (фрагмент)"
125
+ spec:
126
+ template:
127
+ spec:
128
+ nodeSelector:
129
+ preem: 'true' # буде замінено через kustomize
130
+ ```
131
+
77
132
  ## Git branches
78
133
 
79
134
  У **`.github/workflows/clean-merged-branch.yml`** у кроці **`phpdocker-io/github-actions-delete-abandoned-branches`** значення **`with.ignore_branches`** має містити **dev**, **ua** та **ru** (разом з іншими гілками, якщо потрібно), наприклад:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.69",
3
+ "version": "1.8.71",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -14,9 +14,16 @@
14
14
  * Якщо в дереві **k8s** є **HealthCheckPolicy**, перевіряється **`ru/kustomization.yaml`** з patch **`$patch: delete`**
15
15
  * (логіка вмісту — **`ruKustomizationHasHealthCheckDeletePatch`** у **check-k8s.mjs**, узгоджено з **k8s.mdc**).
16
16
  *
17
- * **nodeSelector:** якщо є **Deployment** під **k8s**, у кожному **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`**
17
+ * **nodeSelector (base):** якщо **Deployment** лежить у шляху з сегментом **`base`** (наприклад **`…/k8s/base/deploy.yaml`**),
18
+ * у **`spec.template.spec.nodeSelector`** має бути **`preem`** з булевим значенням **true** або рядком **`'true'`** — overlay **ua** та **ru** далі підміняють селектор.
19
+ *
20
+ * **nodeSelector (overlay):** якщо є **Deployment** під **k8s**, у кожному **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`**
18
21
  * має бути inline **JSON6902** patch на **`kind: Deployment`**: для **ua** — **`op: add`**, **`path: /spec/template/spec/nodeSelector`**,
19
22
  * **`preem: false`**; для **ru** — **`op: replace`**, той самий **path**, **`yandex.cloud/preemptible: false`** (див. abie.mdc).
23
+ *
24
+ * **HTTPRoute nginx-run:** за тієї ж умови (**Deployment** під **k8s**) у **кожному** **`ua`/`ru` kustomization** має бути
25
+ * inline **JSON6902** на **`kind: HTTPRoute`**, **`name: nginx-run`**: **replace** **`/spec/hostnames`** (домени з abie.mdc),
26
+ * **replace** **`/spec/parentRefs/0/namespace`** (**ua** / **ru**); для **ru** також **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`**.
20
27
  */
21
28
  import { existsSync } from 'node:fs'
22
29
  import { readFile } from 'node:fs/promises'
@@ -59,6 +66,60 @@ export function isUaKustomizationPath(rel) {
59
66
  return /(^|\/)ua\/kustomization\.yaml$/u.test(norm)
60
67
  }
61
68
 
69
+ /**
70
+ * Чи відносний шлях до YAML під **k8s** вказує на файл у каталозі **`base`** (сегмент **`base`** у шляху), abie.mdc.
71
+ * @param {string} rel шлях від кореня репозиторію
72
+ * @returns {boolean} true, якщо в шляху є **`/base/`**
73
+ */
74
+ export function isAbieK8sBaseYamlPath(rel) {
75
+ const norm = rel.replaceAll('\\', '/')
76
+ return /(^|\/)base\//u.test(norm)
77
+ }
78
+
79
+ /**
80
+ * Чи значення **`preem`** у base **Deployment** вважається «істинним» за abie.mdc (**true** або рядок **`true`** без урахування регістру).
81
+ * @param {unknown} v значення з YAML
82
+ * @returns {boolean}
83
+ */
84
+ function isAbiePreemTrueish(v) {
85
+ if (v === true) {
86
+ return true
87
+ }
88
+ if (typeof v === 'string' && v.trim().toLowerCase() === 'true') {
89
+ return true
90
+ }
91
+ return false
92
+ }
93
+
94
+ /**
95
+ * Чи документ **Deployment** у **`…/base/…`** містить **`spec.template.spec.nodeSelector.preem`** зі значенням **true** (abie.mdc).
96
+ * @param {unknown} obj корінь YAML-документа (**Deployment**)
97
+ * @returns {boolean} true, якщо критерії виконано
98
+ */
99
+ export function deploymentDocumentHasAbieBasePreemNodeSelector(obj) {
100
+ if (!isDeploymentDoc(obj)) {
101
+ return false
102
+ }
103
+ const rec = /** @type {Record<string, unknown>} */ (obj)
104
+ const spec = rec.spec
105
+ if (spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
106
+ return false
107
+ }
108
+ const template = /** @type {Record<string, unknown>} */ (spec).template
109
+ if (template === null || typeof template !== 'object' || Array.isArray(template)) {
110
+ return false
111
+ }
112
+ const podSpec = /** @type {Record<string, unknown>} */ (template).spec
113
+ if (podSpec === null || typeof podSpec !== 'object' || Array.isArray(podSpec)) {
114
+ return false
115
+ }
116
+ const nodeSelector = /** @type {Record<string, unknown>} */ (podSpec).nodeSelector
117
+ if (nodeSelector === null || typeof nodeSelector !== 'object' || Array.isArray(nodeSelector)) {
118
+ return false
119
+ }
120
+ return isAbiePreemTrueish(nodeSelector.preem)
121
+ }
122
+
62
123
  /**
63
124
  * Чи увімкнено правило **abie** у конфігу репозиторію.
64
125
  * @param {string} root корінь репозиторію (cwd)
@@ -213,6 +274,64 @@ async function collectDeploymentDirs(root, yamlAbs, fail) {
213
274
  return dirs
214
275
  }
215
276
 
277
+ /**
278
+ * Для кожного **Deployment** у YAML під **`k8s`** з шляхом **`…/base/…`** вимагає **`spec.template.spec.nodeSelector.preem: true`** (abie.mdc).
279
+ * @param {string} root корінь репозиторію
280
+ * @param {string[]} yamlFilesAbs yaml під k8s
281
+ * @param {(msg: string) => void} fail callback
282
+ * @returns {Promise<void>}
283
+ */
284
+ async function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail) {
285
+ const baseFiles = yamlFilesAbs.filter(abs => {
286
+ const rel = relative(root, abs).replaceAll('\\', '/') || abs
287
+ return isAbieK8sBaseYamlPath(rel)
288
+ })
289
+ let anyBaseDeployment = false
290
+ for (const abs of baseFiles) {
291
+ const rel = relative(root, abs).replaceAll('\\', '/') || abs
292
+ let raw
293
+ try {
294
+ raw = await readFile(abs, 'utf8')
295
+ } catch (error) {
296
+ const msg = error instanceof Error ? error.message : String(error)
297
+ fail(`${rel}: не вдалося прочитати (${msg})`)
298
+ return
299
+ }
300
+ const body = stripBom(raw)
301
+ const lines = body.split(/\r?\n/u)
302
+ const first = lines[0] ?? ''
303
+ const rest = MODELINE_RE.test(first.trim()) ? lines.slice(1).join('\n') : body
304
+ /** @type {import('yaml').Document[]} */
305
+ let docs
306
+ try {
307
+ docs = parseAllDocuments(rest)
308
+ } catch (error) {
309
+ const msg = error instanceof Error ? error.message : String(error)
310
+ fail(`${rel}: YAML (${msg})`)
311
+ return
312
+ }
313
+ for (const doc of docs) {
314
+ if (doc.errors.length > 0) {
315
+ continue
316
+ }
317
+ const obj = doc.toJSON()
318
+ if (!isDeploymentDoc(obj)) {
319
+ continue
320
+ }
321
+ anyBaseDeployment = true
322
+ if (!deploymentDocumentHasAbieBasePreemNodeSelector(obj)) {
323
+ fail(`${rel}: Deployment у base: потрібен spec.template.spec.nodeSelector.preem: true (або 'true') — abie.mdc`)
324
+ return
325
+ }
326
+ }
327
+ }
328
+ if (anyBaseDeployment) {
329
+ pass('Deployment у …/base/…: nodeSelector.preem відповідає abie.mdc')
330
+ } else {
331
+ pass('Немає Deployment у шляхах …/base/… — перевірку preem у base пропущено')
332
+ }
333
+ }
334
+
216
335
  /**
217
336
  * Прибирає BOM на початку файлу.
218
337
  * @param {string} s вміст
@@ -352,6 +471,131 @@ export function kustomizationHasAbieDeploymentNodeSelectorPatch(raw, mode) {
352
471
  return false
353
472
  }
354
473
 
474
+ /** Домени **hostnames** для overlay **ua** (підрядки у JSON6902-тексті patch), abie.mdc. */
475
+ const ABIE_UA_HTTPROUTE_HOST_MARKERS = ['abie.app', 'vybeerai.com.ua', '*.abie.app', '*.vybeerai.com.ua']
476
+
477
+ /** Домени **hostnames** для overlay **ru** (підрядки), abie.mdc. */
478
+ const ABIE_RU_HTTPROUTE_HOST_MARKERS = [
479
+ 'napitkivmeste.tech',
480
+ 'выбирайонлайн.рф',
481
+ '*.napitkivmeste.tech',
482
+ '*.выбирайонлайн.рф'
483
+ ]
484
+
485
+ /**
486
+ * Збирає тексти inline **patch** для **HTTPRoute/nginx-run** з одного розібраного документа **Kustomization**.
487
+ * @param {import('yaml').Document} doc документ після **parseAllDocuments**
488
+ * @returns {string[]} непорожні рядки **patch**
489
+ */
490
+ function collectNginxRunPatchStringsFromKustomizationDoc(doc) {
491
+ if (doc.errors.length > 0) {
492
+ return []
493
+ }
494
+ const root = doc.toJSON()
495
+ if (root === null || typeof root !== 'object' || Array.isArray(root)) {
496
+ return []
497
+ }
498
+ const rec = /** @type {Record<string, unknown>} */ (root)
499
+ if (rec.kind !== 'Kustomization') {
500
+ return []
501
+ }
502
+ const patches = rec.patches
503
+ if (!Array.isArray(patches)) {
504
+ return []
505
+ }
506
+ /** @type {string[]} */
507
+ const out = []
508
+ for (const p of patches) {
509
+ if (p === null || typeof p !== 'object' || Array.isArray(p)) {
510
+ continue
511
+ }
512
+ const pr = /** @type {Record<string, unknown>} */ (p)
513
+ const target = pr.target
514
+ if (target === null || typeof target !== 'object' || Array.isArray(target)) {
515
+ continue
516
+ }
517
+ const tg = /** @type {Record<string, unknown>} */ (target)
518
+ if (tg.kind !== 'HTTPRoute' || tg.name !== 'nginx-run') {
519
+ continue
520
+ }
521
+ const patchStr = pr.patch
522
+ if (typeof patchStr === 'string' && patchStr.trim() !== '') {
523
+ out.push(patchStr)
524
+ }
525
+ }
526
+ return out
527
+ }
528
+
529
+ /**
530
+ * Об’єднує всі inline **JSON6902**-фрагменти для **HTTPRoute/nginx-run** у **kustomization.yaml** (усі документи у файлі).
531
+ * @param {string} raw повний текст файлу
532
+ * @returns {string} текст для **`validateAbieNginxRunHttpRoutePatches`** (може бути порожнім)
533
+ */
534
+ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
535
+ const body = stripBom(raw)
536
+ const lines = body.split(/\r?\n/u)
537
+ const first = lines[0] ?? ''
538
+ const rest = MODELINE_RE.test(first.trim()) ? lines.slice(1).join('\n') : body
539
+ /** @type {import('yaml').Document[]} */
540
+ let docs
541
+ try {
542
+ docs = parseAllDocuments(rest)
543
+ } catch {
544
+ return ''
545
+ }
546
+ /** @type {string[]} */
547
+ const chunks = []
548
+ for (const doc of docs) {
549
+ chunks.push(...collectNginxRunPatchStringsFromKustomizationDoc(doc))
550
+ }
551
+ return chunks.join('\n')
552
+ }
553
+
554
+ /**
555
+ * Перевіряє об’єднаний текст patch(ів) **HTTPRoute/nginx-run** на відповідність abie.mdc.
556
+ * @param {string} combined текст одного або кількох inline **patch** (з’єднаних перевідом рядка)
557
+ * @param {'ua' | 'ru'} mode **ua** або **ru**
558
+ * @returns {string | null} повідомлення про помилку або **null**
559
+ */
560
+ export function validateAbieNginxRunHttpRoutePatches(combined, mode) {
561
+ if (typeof combined !== 'string' || combined.trim() === '') {
562
+ return `очікується patch target kind HTTPRoute name nginx-run (replace hostnames, parentRefs namespace ${mode}; для ru — також gwin… upgradeTypes websocket) — abie.mdc`
563
+ }
564
+ const hasHostnamesReplace = /-\s*op:\s*replace\b[\s\S]{0,200}?path:\s*\/spec\/hostnames\b/m.test(combined)
565
+ if (!hasHostnamesReplace) {
566
+ return 'HTTPRoute nginx-run: потрібен блок op replace з path /spec/hostnames (abie.mdc)'
567
+ }
568
+ const markers = mode === 'ua' ? ABIE_UA_HTTPROUTE_HOST_MARKERS : ABIE_RU_HTTPROUTE_HOST_MARKERS
569
+ if (!markers.some(m => combined.includes(m))) {
570
+ return `HTTPRoute nginx-run: у value для /spec/hostnames має бути один із доменів abie (${markers.join(', ')}) — abie.mdc`
571
+ }
572
+ const ns = mode === 'ua' ? 'ua' : 'ru'
573
+ const nsRe = new RegExp(
574
+ String.raw`path:\s*\/spec\/parentRefs\/0\/namespace\b[\s\S]{0,200}?value:\s*['"]?${ns}['"]?(?:\s|$)`,
575
+ 'mu'
576
+ )
577
+ if (!nsRe.test(combined)) {
578
+ return `HTTPRoute nginx-run: потрібен replace path /spec/parentRefs/0/namespace з value ${ns} (abie.mdc)`
579
+ }
580
+ if (mode === 'ru') {
581
+ if (!/gwin\.yandex\.cloud\/rules\.http\.upgradeTypes:\s*['"]?websocket['"]?/m.test(combined)) {
582
+ return 'HTTPRoute nginx-run (ru): потрібна анотація gwin.yandex.cloud/rules.http.upgradeTypes: websocket (abie.mdc)'
583
+ }
584
+ }
585
+ return null
586
+ }
587
+
588
+ /**
589
+ * Чи **kustomization** містить валідні для abie патчі **HTTPRoute/nginx-run** (**ua** або **ru**).
590
+ * @param {string} raw повний текст **kustomization.yaml**
591
+ * @param {'ua' | 'ru'} mode overlay
592
+ * @returns {boolean} true, якщо **`validateAbieNginxRunHttpRoutePatches`** повертає **null**
593
+ */
594
+ export function kustomizationHasAbieNginxRunHttpRoutePatch(raw, mode) {
595
+ const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
596
+ return validateAbieNginxRunHttpRoutePatches(combined, mode) === null
597
+ }
598
+
355
599
  /**
356
600
  * Перевіряє **hc.yaml** на відповідність abie.mdc.
357
601
  * @param {string} raw повний текст файлу
@@ -592,6 +836,67 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail) {
592
836
  }
593
837
  }
594
838
 
839
+ /**
840
+ * Якщо є **Deployment** під **k8s**, вимагає в кожному overlay **ua** та **ru** patch **HTTPRoute/nginx-run** (abie.mdc).
841
+ * @param {string} root корінь репозиторію
842
+ * @param {string[]} yamlFilesAbs yaml під k8s
843
+ * @param {(msg: string) => void} fail callback
844
+ * @returns {Promise<void>}
845
+ */
846
+ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail) {
847
+ const uaAbsList = yamlFilesAbs.filter(abs => isUaKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
848
+ if (uaAbsList.length === 0) {
849
+ fail(
850
+ 'Є Deployment у k8s — додай ua/kustomization.yaml з patch HTTPRoute nginx-run (hostnames, parentRefs namespace ua) — abie.mdc'
851
+ )
852
+ return
853
+ }
854
+ for (const abs of uaAbsList) {
855
+ const rel = relative(root, abs).replaceAll('\\', '/') || abs
856
+ let raw
857
+ try {
858
+ raw = await readFile(abs, 'utf8')
859
+ } catch (error) {
860
+ const msg = error instanceof Error ? error.message : String(error)
861
+ fail(`${rel}: не вдалося прочитати (${msg})`)
862
+ return
863
+ }
864
+ const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
865
+ const v = validateAbieNginxRunHttpRoutePatches(combined, 'ua')
866
+ if (v !== null) {
867
+ fail(`${rel}: ${v}`)
868
+ return
869
+ }
870
+ pass(`${rel}: HTTPRoute nginx-run (ua) відповідає abie.mdc`)
871
+ }
872
+
873
+ const ruAbsList = yamlFilesAbs.filter(abs => isRuKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
874
+ if (ruAbsList.length === 0) {
875
+ fail(
876
+ 'Є Deployment у k8s — додай ru/kustomization.yaml з patch HTTPRoute nginx-run (hostnames, namespace ru, gwin websocket) — abie.mdc'
877
+ )
878
+ return
879
+ }
880
+ for (const abs of ruAbsList) {
881
+ const rel = relative(root, abs).replaceAll('\\', '/') || abs
882
+ let raw
883
+ try {
884
+ raw = await readFile(abs, 'utf8')
885
+ } catch (error) {
886
+ const msg = error instanceof Error ? error.message : String(error)
887
+ fail(`${rel}: не вдалося прочитати (${msg})`)
888
+ return
889
+ }
890
+ const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
891
+ const v = validateAbieNginxRunHttpRoutePatches(combined, 'ru')
892
+ if (v !== null) {
893
+ fail(`${rel}: ${v}`)
894
+ return
895
+ }
896
+ pass(`${rel}: HTTPRoute nginx-run (ru) відповідає abie.mdc`)
897
+ }
898
+ }
899
+
595
900
  /**
596
901
  * Перевіряє відповідність проєкту правилам abie.mdc.
597
902
  * @returns {Promise<number>} 0 — OK, 1 — є порушення
@@ -672,6 +977,8 @@ export async function check() {
672
977
  )
673
978
  }
674
979
  }
980
+ pass('Є Deployment — перевіряємо base: spec.template.spec.nodeSelector.preem (abie.mdc)')
981
+ await ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFiles, fail)
675
982
  } else {
676
983
  pass('Немає Deployment у дереві k8s — перевірку hc.yaml пропущено')
677
984
  }
@@ -682,6 +989,8 @@ export async function check() {
682
989
  if (deploymentDirs.size > 0) {
683
990
  pass('Є Deployment — перевіряємо nodeSelector у ua/ru kustomization (abie.mdc)')
684
991
  await ensureUaRuAbieNodeSelectorPatches(root, yamlFiles, fail)
992
+ pass('Є Deployment — перевіряємо HTTPRoute nginx-run у ua/ru kustomization (abie.mdc)')
993
+ await ensureUaRuAbieHttpRoutePatches(root, yamlFiles, fail)
685
994
  }
686
995
 
687
996
  return exitCode