@nitra/cursor 1.8.84 → 1.8.86
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 +36 -2
- package/mdc/k8s.mdc +2 -0
- package/package.json +1 -1
- package/scripts/check-abie.mjs +195 -3
- package/scripts/check-k8s.mjs +41 -2
package/mdc/abie.mdc
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила для проєктів AbInBev Efes
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.10'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** / **ru** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**), видалення **HealthCheckPolicy** у **ru**), гілки **dev**, **ua**, **ru** у **clean-merged-branch**, а також заборона артефактів **Firebase Hosting** у корені репозиторію.
|
|
7
|
+
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** / **ru** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`filelint-hl`** — **`namespace: dev`** у base та patch **`…/backendRefs/…/namespace`** у **ua** / **ru**), видалення **HealthCheckPolicy** у **ru**), гілки **dev**, **ua**, **ru** у **clean-merged-branch**, а також заборона артефактів **Firebase Hosting** у корені репозиторію.
|
|
8
8
|
|
|
9
9
|
**`npx @nitra/cursor check abie`** виконується лише якщо в **`.n-cursor.json`** у **`rules`** є **`abie`** — інакше вихід **0** без зауважень.
|
|
10
10
|
|
|
@@ -38,6 +38,28 @@ spec:
|
|
|
38
38
|
|
|
39
39
|
За наявності **Deployment** під **k8s** і наявності **Vite** (**`vite.config.js`**, **`vite.config.mjs`** або **`vite.config.ts`** у каталозі пакета) у **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`** цього пакета потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`** / **`ru`**). Для **ru** — анотація **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`** лише якщо в **тому ж** **`ru/kustomization.yaml`** є згадка **`HASURA_GRAPHQL_JWT_SECRET`** (типово patch на **ConfigMap** Hasura). Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
|
|
40
40
|
|
|
41
|
+
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`filelint-hl`**
|
|
42
|
+
|
|
43
|
+
Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** та **`ru/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
|
|
44
|
+
|
|
45
|
+
```yaml title="…/k8s/base/hr.yaml (фрагмент)"
|
|
46
|
+
spec:
|
|
47
|
+
rules:
|
|
48
|
+
- matches:
|
|
49
|
+
- path:
|
|
50
|
+
type: PathPrefix
|
|
51
|
+
value: /
|
|
52
|
+
backendRefs:
|
|
53
|
+
- name: auth-run-hl
|
|
54
|
+
namespace: dev
|
|
55
|
+
port: 8080
|
|
56
|
+
- name: filelint-hl
|
|
57
|
+
namespace: dev
|
|
58
|
+
port: 8080
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
У **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`** додай до того самого **inline** patch на **`HTTPRoute`** (той самий **`target.name`**) операції **JSON6902** з **`path`**: **`/spec/rules/<i>/backendRefs/<j>/namespace`**, де **`<i>`** / **`<j>`** — індекси відповідно до порядку **`spec.rules`** та **`backendRefs`** у base-файлі; **`value`**: **`ua`** або **`ru`**. Якщо кілька таких **`backendRefs`**, потрібна окрема операція для кожного.
|
|
62
|
+
|
|
41
63
|
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
42
64
|
- target:
|
|
43
65
|
kind: HTTPRoute
|
|
@@ -50,6 +72,12 @@ spec:
|
|
|
50
72
|
- op: replace
|
|
51
73
|
path: /spec/parentRefs/0/namespace
|
|
52
74
|
value: ua
|
|
75
|
+
- op: replace
|
|
76
|
+
path: /spec/rules/0/backendRefs/0/namespace
|
|
77
|
+
value: ua
|
|
78
|
+
- op: replace
|
|
79
|
+
path: /spec/rules/0/backendRefs/1/namespace
|
|
80
|
+
value: ua
|
|
53
81
|
```
|
|
54
82
|
|
|
55
83
|
```yaml title="…/ru/kustomization.yaml (фрагмент)"
|
|
@@ -64,6 +92,12 @@ spec:
|
|
|
64
92
|
- op: replace
|
|
65
93
|
path: /spec/parentRefs/0/namespace
|
|
66
94
|
value: ru
|
|
95
|
+
- op: replace
|
|
96
|
+
path: /spec/rules/0/backendRefs/0/namespace
|
|
97
|
+
value: ru
|
|
98
|
+
- op: replace
|
|
99
|
+
path: /spec/rules/0/backendRefs/1/namespace
|
|
100
|
+
value: ru
|
|
67
101
|
```
|
|
68
102
|
|
|
69
103
|
Якщо в цьому ж файлі є **`HASURA_GRAPHQL_JWT_SECRET`** (Hasura з JWT), додай окремий patch на **HTTPRoute** з анотацією для WebSocket:
|
package/mdc/k8s.mdc
CHANGED
|
@@ -15,6 +15,8 @@ alwaysApply: false
|
|
|
15
15
|
|
|
16
16
|
Далі — вміст маніфесту. Зайвий порожній рядок між коментарем і YAML не додавай, якщо в проєкті не прийнято інше.
|
|
17
17
|
|
|
18
|
+
**Виняток — без modeline:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`check-k8s.mjs`**.
|
|
19
|
+
|
|
18
20
|
**Розширення:** усі маніфести під **`k8s`**, включно з **`kustomization.yaml`**, — лише **`.yaml`** (розширення **`.yml`** не використовуй).
|
|
19
21
|
|
|
20
22
|
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor check docker`**.
|
package/package.json
CHANGED
package/scripts/check-abie.mjs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* **k8s:** якщо під деревом із сегментом **`k8s`** є YAML з **`kind: Deployment`**, у тій самій директорії
|
|
14
14
|
* має існувати **`hc.yaml`** із **`HealthCheckPolicy`** (**`networking.gke.io/v1`**), modeline **`$schema`**
|
|
15
15
|
* як у abie.mdc, **`/healthz`**, порт **8080**, **`targetRef`** на **Service** з тим самим **`metadata.name`**.
|
|
16
|
+
* Загальні вимоги до **`# yaml-language-server: $schema`** для інших YAML під **`k8s`** — у **check-k8s.mjs** / **k8s.mdc** (наприклад **HttpBackendGroup** `alb.yc.io/v1alpha1` — **без** modeline).
|
|
16
17
|
* Якщо в дереві **k8s** є **HealthCheckPolicy**, перевіряється **`ru/kustomization.yaml`** з patch **`$patch: delete`**
|
|
17
18
|
* (логіка вмісту — **`ruKustomizationHasHealthCheckDeletePatch`** у **check-k8s.mjs**, узгоджено з **k8s.mdc**).
|
|
18
19
|
*
|
|
@@ -27,6 +28,8 @@
|
|
|
27
28
|
* — тоді в **`ua`/`ru` kustomization** потрібен patch на **`kind: HTTPRoute`**, **непорожній `target.name`**: **`/spec/hostnames`**
|
|
28
29
|
* (домени abie.mdc), **`/spec/parentRefs/0/namespace`** (**ua** / **ru**); для **ru** — **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`**,
|
|
29
30
|
* якщо в тому ж **`kustomization.yaml`** згадується **`HASURA_GRAPHQL_JWT_SECRET`** (Hasura + JWT).
|
|
31
|
+
* **Спільні бекенди (`auth-run-hl`, `filelint-hl`):** у **HTTPRoute** під **`k8s`** поза overlay **ua** та **ru** (шлях не містить **`k8s/ua/`** чи **`k8s/ru/`**) кожен такий **`backendRefs`** має **`namespace: dev`** і порт **8080**;
|
|
32
|
+
* у patch overlay **ua** та **ru** — по одному **JSON6902** на **`/spec/rules/…/backendRefs/…/namespace`** з **`value`**: **ua** або **ru** (кількість patch-ів = кількість таких **`backendRefs`** у пакеті).
|
|
30
33
|
* Вибір **`op`** — **k8s.mdc**.
|
|
31
34
|
*/
|
|
32
35
|
import { existsSync } from 'node:fs'
|
|
@@ -45,6 +48,14 @@ const CONFIG_FILE = '.n-cursor.json'
|
|
|
45
48
|
/** Маркер у kustomization.yaml: якщо зустрічається у файлі — для overlay ru у patch HTTPRoute потрібна анотація gwin…websocket. */
|
|
46
49
|
const HASURA_JWT_SECRET_IN_KUSTOMIZATION = 'HASURA_GRAPHQL_JWT_SECRET'
|
|
47
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Спільні **Service** (**`-hl`**) у **dev**: у base-**HTTPRoute** обов’язково **`namespace: dev`**, у overlay — patch **`…/backendRefs/…/namespace`** (abie.mdc).
|
|
53
|
+
* Експорт для споживачів / тестів.
|
|
54
|
+
*/
|
|
55
|
+
export const ABIE_SHARED_CROSS_NS_BACKEND_NAMES = Object.freeze(['auth-run-hl', 'filelint-hl'])
|
|
56
|
+
|
|
57
|
+
const ABIE_SHARED_CROSS_NS_BACKEND_SET = new Set(ABIE_SHARED_CROSS_NS_BACKEND_NAMES)
|
|
58
|
+
|
|
48
59
|
/** Очікуваний URL **`$schema`** для **hc.yaml** (abie.mdc). */
|
|
49
60
|
export const ABIE_HC_SCHEMA_URL = 'https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json'
|
|
50
61
|
|
|
@@ -527,6 +538,136 @@ export function kustomizationHasAbieDeploymentNodeSelectorPatch(raw, mode) {
|
|
|
527
538
|
return false
|
|
528
539
|
}
|
|
529
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Чи YAML відносно кореня належить до **`${pkgRel}/k8s/**`** поза піддеревами **`ua/`** та **`ru/`** (base-шар abie).
|
|
543
|
+
* @param {string} relFromRoot відносний шлях від кореня
|
|
544
|
+
* @param {string} pkgRelFromRoot каталог пакета відносно кореня (без завершального слеша після імені пакета)
|
|
545
|
+
* @returns {boolean}
|
|
546
|
+
*/
|
|
547
|
+
export function isK8sYamlInAbiePackageExcludingUaRuOverlays(relFromRoot, pkgRelFromRoot) {
|
|
548
|
+
const normRel = relFromRoot.replaceAll('\\', '/')
|
|
549
|
+
const pkg = pkgRelFromRoot.replaceAll('\\', '/').replace(/\/$/u, '')
|
|
550
|
+
const prefix = `${pkg}/k8s/`
|
|
551
|
+
if (!normRel.startsWith(prefix)) {
|
|
552
|
+
return false
|
|
553
|
+
}
|
|
554
|
+
const after = normRel.slice(prefix.length)
|
|
555
|
+
return !after.startsWith('ua/') && !after.startsWith('ru/')
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* З HTTPRoute-документа рахує **`backendRefs`** до **`auth-run-hl`** / **`filelint-hl`** і порушення **`namespace: dev`**.
|
|
560
|
+
* @param {unknown} obj корінь YAML
|
|
561
|
+
* @param {string} rel відносний шлях (повідомлення)
|
|
562
|
+
* @returns {{ refCount: number, errors: string[] }}
|
|
563
|
+
*/
|
|
564
|
+
function httpRouteDocSharedCrossNsBackendStats(obj, rel) {
|
|
565
|
+
/** @type {string[]} */
|
|
566
|
+
const errors = []
|
|
567
|
+
if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
568
|
+
return { refCount: 0, errors }
|
|
569
|
+
}
|
|
570
|
+
const rec = /** @type {Record<string, unknown>} */ (obj)
|
|
571
|
+
if (rec.kind !== 'HTTPRoute') {
|
|
572
|
+
return { refCount: 0, errors }
|
|
573
|
+
}
|
|
574
|
+
const spec = rec.spec
|
|
575
|
+
if (spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
|
|
576
|
+
return { refCount: 0, errors }
|
|
577
|
+
}
|
|
578
|
+
const rules = /** @type {Record<string, unknown>} */ (spec).rules
|
|
579
|
+
if (!Array.isArray(rules)) {
|
|
580
|
+
return { refCount: 0, errors }
|
|
581
|
+
}
|
|
582
|
+
let refCount = 0
|
|
583
|
+
for (const rule of rules) {
|
|
584
|
+
if (rule === null || typeof rule !== 'object' || Array.isArray(rule)) {
|
|
585
|
+
continue
|
|
586
|
+
}
|
|
587
|
+
const brs = /** @type {Record<string, unknown>} */ (rule).backendRefs
|
|
588
|
+
if (!Array.isArray(brs)) {
|
|
589
|
+
continue
|
|
590
|
+
}
|
|
591
|
+
for (const br of brs) {
|
|
592
|
+
if (br === null || typeof br !== 'object' || Array.isArray(br)) {
|
|
593
|
+
continue
|
|
594
|
+
}
|
|
595
|
+
const brRec = /** @type {Record<string, unknown>} */ (br)
|
|
596
|
+
const name = brRec.name
|
|
597
|
+
if (typeof name !== 'string' || !ABIE_SHARED_CROSS_NS_BACKEND_SET.has(name)) {
|
|
598
|
+
continue
|
|
599
|
+
}
|
|
600
|
+
refCount++
|
|
601
|
+
const ns = brRec.namespace
|
|
602
|
+
if (typeof ns !== 'string' || ns !== 'dev') {
|
|
603
|
+
errors.push(`${rel}: HTTPRoute backendRefs до ${name} має містити namespace: dev (abie.mdc)`)
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return { refCount, errors }
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* З YAML під **k8s** пакета (без overlay **ua** та **ru**) збирає кількість **`backendRefs`** до **`auth-run-hl`** і **`filelint-hl`** і порушення **`namespace: dev`**.
|
|
612
|
+
* @param {string} root корінь репозиторію
|
|
613
|
+
* @param {string} pkgAbs абсолютний шлях до каталогу пакета
|
|
614
|
+
* @param {string[]} yamlFilesAbs усі **yaml** під **k8s** (як **findK8sYamlFiles**)
|
|
615
|
+
* @returns {Promise<{ refCount: number, baseErrors: string[] }>}
|
|
616
|
+
*/
|
|
617
|
+
export async function analyzeAbieSharedBackendRefsInPackageK8s(root, pkgAbs, yamlFilesAbs) {
|
|
618
|
+
const pkgRel = relative(root, pkgAbs).replaceAll('\\', '/') || pkgAbs
|
|
619
|
+
let refCount = 0
|
|
620
|
+
/** @type {string[]} */
|
|
621
|
+
const baseErrors = []
|
|
622
|
+
for (const abs of yamlFilesAbs) {
|
|
623
|
+
const rel = relative(root, abs).replaceAll('\\', '/') || abs
|
|
624
|
+
if (!isK8sYamlInAbiePackageExcludingUaRuOverlays(rel, pkgRel)) {
|
|
625
|
+
continue
|
|
626
|
+
}
|
|
627
|
+
let raw
|
|
628
|
+
try {
|
|
629
|
+
raw = await readFile(abs, 'utf8')
|
|
630
|
+
} catch {
|
|
631
|
+
continue
|
|
632
|
+
}
|
|
633
|
+
const body = stripBom(raw)
|
|
634
|
+
const lines = body.split(/\r?\n/u)
|
|
635
|
+
const first = lines[0] ?? ''
|
|
636
|
+
const rest = MODELINE_RE.test(first.trim()) ? lines.slice(1).join('\n') : body
|
|
637
|
+
/** @type {import('yaml').Document[]} */
|
|
638
|
+
let docs
|
|
639
|
+
try {
|
|
640
|
+
docs = parseAllDocuments(rest)
|
|
641
|
+
} catch {
|
|
642
|
+
continue
|
|
643
|
+
}
|
|
644
|
+
for (const doc of docs) {
|
|
645
|
+
if (doc.errors.length > 0) {
|
|
646
|
+
continue
|
|
647
|
+
}
|
|
648
|
+
const obj = doc.toJSON()
|
|
649
|
+
const st = httpRouteDocSharedCrossNsBackendStats(obj, rel)
|
|
650
|
+
refCount += st.refCount
|
|
651
|
+
baseErrors.push(...st.errors)
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return { refCount, baseErrors }
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Рахує операції JSON6902 з **`path`**: **`/spec/rules/…/backendRefs/…/namespace`** та **`value`** overlay.
|
|
659
|
+
* @param {string} combined сукупний текст patch **HTTPRoute**
|
|
660
|
+
* @param {'ua' | 'ru'} mode overlay
|
|
661
|
+
* @returns {number}
|
|
662
|
+
*/
|
|
663
|
+
function countAbieHttpRouteBackendRefNamespacePatchesInCombined(combined, mode) {
|
|
664
|
+
const re =
|
|
665
|
+
mode === 'ua'
|
|
666
|
+
? /path:\s*\/spec\/rules\/\d+\/backendRefs\/\d+\/namespace\b[\s\S]{0,200}?value:\s*['"]?ua['"]?(?:\s|$)/gmu
|
|
667
|
+
: /path:\s*\/spec\/rules\/\d+\/backendRefs\/\d+\/namespace\b[\s\S]{0,200}?value:\s*['"]?ru['"]?(?:\s|$)/gmu
|
|
668
|
+
return [...combined.matchAll(re)].length
|
|
669
|
+
}
|
|
670
|
+
|
|
530
671
|
/** Домени **hostnames** для overlay **ua** (підрядки у JSON6902-тексті patch), abie.mdc. */
|
|
531
672
|
const ABIE_UA_HTTPROUTE_HOST_MARKERS = ['abie.app', 'vybeerai.com.ua', '*.abie.app', '*.vybeerai.com.ua']
|
|
532
673
|
|
|
@@ -609,9 +750,15 @@ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
|
|
|
609
750
|
* @param {string} combined текст одного або кількох inline **patch**, розділених символом нового рядка
|
|
610
751
|
* @param {'ua' | 'ru'} mode **ua** або **ru**
|
|
611
752
|
* @param {string} [fullKustomizationRaw] повний текст **kustomization.yaml** — для **ru** визначає, чи потрібна анотація **gwin…websocket** (лише якщо є **`HASURA_GRAPHQL_JWT_SECRET`**)
|
|
753
|
+
* @param {number} [sharedCrossNsBackendRefCount] скільки **`backendRefs`** до **`auth-run-hl`** і **`filelint-hl`** у base **HTTPRoute** пакета — стільки ж patch-ів **`…/backendRefs/…/namespace`** з **`value`** overlay
|
|
612
754
|
* @returns {string | null} повідомлення про помилку або **null**
|
|
613
755
|
*/
|
|
614
|
-
export function validateAbieNginxRunHttpRoutePatches(
|
|
756
|
+
export function validateAbieNginxRunHttpRoutePatches(
|
|
757
|
+
combined,
|
|
758
|
+
mode,
|
|
759
|
+
fullKustomizationRaw,
|
|
760
|
+
sharedCrossNsBackendRefCount = 0
|
|
761
|
+
) {
|
|
615
762
|
if (typeof combined !== 'string' || combined.trim() === '') {
|
|
616
763
|
return `очікується patch target kind HTTPRoute з непорожнім target.name (hostnames, parentRefs namespace ${mode}; для ru — gwin… websocket лише за наявності HASURA_GRAPHQL_JWT_SECRET у файлі) — abie.mdc`
|
|
617
764
|
}
|
|
@@ -636,6 +783,16 @@ export function validateAbieNginxRunHttpRoutePatches(combined, mode, fullKustomi
|
|
|
636
783
|
if (ruNeedsWebsocket && !/gwin\.yandex\.cloud\/rules\.http\.upgradeTypes:\s*['"]?websocket['"]?/m.test(combined)) {
|
|
637
784
|
return 'HTTPRoute (ru): за наявності HASURA_GRAPHQL_JWT_SECRET у kustomization потрібна анотація gwin.yandex.cloud/rules.http.upgradeTypes: websocket (abie.mdc)'
|
|
638
785
|
}
|
|
786
|
+
const sharedCount =
|
|
787
|
+
typeof sharedCrossNsBackendRefCount === 'number' && Number.isFinite(sharedCrossNsBackendRefCount)
|
|
788
|
+
? Math.max(0, Math.floor(sharedCrossNsBackendRefCount))
|
|
789
|
+
: 0
|
|
790
|
+
if (sharedCount > 0) {
|
|
791
|
+
const patchHits = countAbieHttpRouteBackendRefNamespacePatchesInCombined(combined, mode)
|
|
792
|
+
if (patchHits < sharedCount) {
|
|
793
|
+
return `HTTPRoute: для backendRefs до спільних сервісів auth-run-hl, filelint-hl очікується ${sharedCount} JSON6902 patch(ів) з path /spec/rules/…/backendRefs/…/namespace та value ${mode} (зараз ${patchHits}) — abie.mdc`
|
|
794
|
+
}
|
|
795
|
+
}
|
|
639
796
|
return null
|
|
640
797
|
}
|
|
641
798
|
|
|
@@ -911,6 +1068,21 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, deploymentD
|
|
|
911
1068
|
* @returns {Promise<void>}
|
|
912
1069
|
*/
|
|
913
1070
|
async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn) {
|
|
1071
|
+
/** @type {Map<string, Promise<{ refCount: number, baseErrors: string[] }>>} */
|
|
1072
|
+
const sharedBackendAnalysisByPkg = new Map()
|
|
1073
|
+
/**
|
|
1074
|
+
* @param {string} pkgAbs
|
|
1075
|
+
* @returns {Promise<{ refCount: number, baseErrors: string[] }>}
|
|
1076
|
+
*/
|
|
1077
|
+
const getSharedBackendAnalysis = pkgAbs => {
|
|
1078
|
+
let p = sharedBackendAnalysisByPkg.get(pkgAbs)
|
|
1079
|
+
if (!p) {
|
|
1080
|
+
p = analyzeAbieSharedBackendRefsInPackageK8s(root, pkgAbs, yamlFilesAbs)
|
|
1081
|
+
sharedBackendAnalysisByPkg.set(pkgAbs, p)
|
|
1082
|
+
}
|
|
1083
|
+
return p
|
|
1084
|
+
}
|
|
1085
|
+
|
|
914
1086
|
const uaAbsList = yamlFilesAbs.filter(abs => isUaKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
915
1087
|
if (uaAbsList.length === 0) {
|
|
916
1088
|
passFn(
|
|
@@ -920,6 +1092,16 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
920
1092
|
for (const abs of uaAbsList) {
|
|
921
1093
|
const rel = relative(root, abs).replaceAll('\\', '/') || abs
|
|
922
1094
|
if (abieOverlayRequiresHttpRouteByVite(root, abs)) {
|
|
1095
|
+
const pkgAbs = abiePackageDirFromK8sOverlay(root, abs)
|
|
1096
|
+
if (!pkgAbs) {
|
|
1097
|
+
fail(`${rel}: внутрішня помилка abie overlay (немає каталогу пакета)`)
|
|
1098
|
+
return
|
|
1099
|
+
}
|
|
1100
|
+
const sharedAnalysis = await getSharedBackendAnalysis(pkgAbs)
|
|
1101
|
+
for (const err of sharedAnalysis.baseErrors) {
|
|
1102
|
+
fail(err)
|
|
1103
|
+
return
|
|
1104
|
+
}
|
|
923
1105
|
let raw
|
|
924
1106
|
try {
|
|
925
1107
|
raw = await readFile(abs, 'utf8')
|
|
@@ -929,7 +1111,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
929
1111
|
return
|
|
930
1112
|
}
|
|
931
1113
|
const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
|
|
932
|
-
const v = validateAbieNginxRunHttpRoutePatches(combined, 'ua')
|
|
1114
|
+
const v = validateAbieNginxRunHttpRoutePatches(combined, 'ua', raw, sharedAnalysis.refCount)
|
|
933
1115
|
if (v !== null) {
|
|
934
1116
|
fail(`${rel}: ${v}`)
|
|
935
1117
|
return
|
|
@@ -949,6 +1131,16 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
949
1131
|
for (const abs of ruAbsList) {
|
|
950
1132
|
const rel = relative(root, abs).replaceAll('\\', '/') || abs
|
|
951
1133
|
if (abieOverlayRequiresHttpRouteByVite(root, abs)) {
|
|
1134
|
+
const pkgAbs = abiePackageDirFromK8sOverlay(root, abs)
|
|
1135
|
+
if (!pkgAbs) {
|
|
1136
|
+
fail(`${rel}: внутрішня помилка abie overlay (немає каталогу пакета)`)
|
|
1137
|
+
return
|
|
1138
|
+
}
|
|
1139
|
+
const sharedAnalysis = await getSharedBackendAnalysis(pkgAbs)
|
|
1140
|
+
for (const err of sharedAnalysis.baseErrors) {
|
|
1141
|
+
fail(err)
|
|
1142
|
+
return
|
|
1143
|
+
}
|
|
952
1144
|
let raw
|
|
953
1145
|
try {
|
|
954
1146
|
raw = await readFile(abs, 'utf8')
|
|
@@ -958,7 +1150,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
958
1150
|
return
|
|
959
1151
|
}
|
|
960
1152
|
const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
|
|
961
|
-
const v = validateAbieNginxRunHttpRoutePatches(combined, 'ru', raw)
|
|
1153
|
+
const v = validateAbieNginxRunHttpRoutePatches(combined, 'ru', raw, sharedAnalysis.refCount)
|
|
962
1154
|
if (v !== null) {
|
|
963
1155
|
fail(`${rel}: ${v}`)
|
|
964
1156
|
return
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Перший рядок `# yaml-language-server: $schema=…`, без дублікатів, розширення `.yaml`
|
|
5
5
|
* (окрім `kustomization.yaml`); URL схеми за першим документом — kustomization / yannh / datree
|
|
6
|
+
* (**виняток:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` — рядка `# yaml-language-server:` у файлі бути не має).
|
|
6
7
|
* (datree за замовчуванням: GitHub Pages `https://datreeio.github.io/CRDs-catalog/…`).
|
|
7
8
|
*
|
|
8
9
|
* Додатково: у кожному YAML-документі з **`kind: Deployment`** у кожного контейнера
|
|
@@ -671,6 +672,18 @@ function extractApiVersionAndKind(doc) {
|
|
|
671
672
|
}
|
|
672
673
|
}
|
|
673
674
|
|
|
675
|
+
/**
|
|
676
|
+
* Чи перший YAML-документ (до `---`) — **HttpBackendGroup** з API **alb.yc.io/v1alpha1** (Yandex ALB).
|
|
677
|
+
* Для таких файлів **check-k8s** не вимагає modeline `# yaml-language-server: $schema=…` і забороняє його.
|
|
678
|
+
* @param {string} yamlBody вміст файлу або фрагмент після modeline
|
|
679
|
+
* @returns {boolean} true, якщо `apiVersion`/`kind` першого документа збігаються з винятком
|
|
680
|
+
*/
|
|
681
|
+
export function k8sYamlFirstDocIsAlbYcHttpBackendGroup(yamlBody) {
|
|
682
|
+
const first = firstYamlDocument(yamlBody)
|
|
683
|
+
const { apiVersion, kind } = extractApiVersionAndKind(first)
|
|
684
|
+
return apiVersion === 'alb.yc.io/v1alpha1' && kind === 'HttpBackendGroup'
|
|
685
|
+
}
|
|
686
|
+
|
|
674
687
|
/**
|
|
675
688
|
* Чи вміст overlay **`ru/kustomization.yaml`** містить Kustomize patch видалення **HealthCheckPolicy**.
|
|
676
689
|
* @param {string} raw повний текст файлу
|
|
@@ -1599,12 +1612,38 @@ async function checkK8sYamlFile(abs, root, fail, pass, kustomizeManagedRel) {
|
|
|
1599
1612
|
return
|
|
1600
1613
|
}
|
|
1601
1614
|
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1615
|
+
const firstLineIsModeline = MODELINE_RE.test(lines[0])
|
|
1616
|
+
const bodyForFirstDoc = k8sYamlBodyForDocumentParse(lines)
|
|
1617
|
+
const isAlbHttpBackendGroup = k8sYamlFirstDocIsAlbYcHttpBackendGroup(bodyForFirstDoc)
|
|
1618
|
+
|
|
1619
|
+
if (isAlbHttpBackendGroup) {
|
|
1620
|
+
if (firstLineIsModeline) {
|
|
1621
|
+
fail(
|
|
1622
|
+
`${rel}: для kind HttpBackendGroup (apiVersion alb.yc.io/v1alpha1) не задавай # yaml-language-server: $schema — прибери перший рядок modeline (k8s.mdc)`
|
|
1623
|
+
)
|
|
1624
|
+
return
|
|
1625
|
+
}
|
|
1626
|
+
if (countSchemaModelines(lines) > 0) {
|
|
1627
|
+
fail(
|
|
1628
|
+
`${rel}: для kind HttpBackendGroup (apiVersion alb.yc.io/v1alpha1) не використовуй # yaml-language-server: $schema у файлі (k8s.mdc)`
|
|
1629
|
+
)
|
|
1630
|
+
return
|
|
1631
|
+
}
|
|
1632
|
+
const body = lines.join('\n')
|
|
1633
|
+
scanIngressInYamlDocuments(rel, body, fail)
|
|
1634
|
+
pass(`${rel}: HttpBackendGroup (alb.yc.io/v1alpha1) — modeline $schema не застосовується (k8s.mdc)`)
|
|
1635
|
+
const kustomizeManaged = kustomizeManagedRel.has(rel)
|
|
1636
|
+
validateK8sYamlPolicyDocuments(rel, baseLower, body, fail, kustomizeManaged)
|
|
1637
|
+
scanGatewayApiRouteBackendRefsInYamlBody(rel, body, fail)
|
|
1638
|
+
return
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
if (!firstLineIsModeline) {
|
|
1604
1642
|
fail(`${rel}: перший рядок має бути коментарем # yaml-language-server: $schema=<url> (без префіксів перед #)`)
|
|
1605
1643
|
return
|
|
1606
1644
|
}
|
|
1607
1645
|
|
|
1646
|
+
const m = /** @type {RegExpMatchArray} */ (lines[0].match(MODELINE_RE))
|
|
1608
1647
|
const schemaUrl = m[1]
|
|
1609
1648
|
if (countSchemaModelines(lines) > 1) {
|
|
1610
1649
|
fail(`${rel}: кілька рядків yaml-language-server $schema — лиш один modeline на файл (див. k8s.mdc)`)
|