@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 +56 -1
- package/package.json +1 -1
- package/scripts/check-abie.mjs +310 -1
package/mdc/abie.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила для проєктів AbInBev Efes
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
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
package/scripts/check-abie.mjs
CHANGED
|
@@ -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:** якщо
|
|
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
|